# Always print this out before your assignment
sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods  
[7] base     

other attached packages:
 [1] ggridges_0.5.3             glmnetUtils_1.1.8         
 [3] glmnet_4.1-3               Matrix_1.3-4              
 [5] plotROC_2.2.1              scales_1.1.1              
 [7] tidyquant_1.0.3            quantmod_0.4.18           
 [9] TTR_0.24.2                 PerformanceAnalytics_2.0.4
[11] xts_0.12.1                 zoo_1.8-9                 
[13] plotly_4.10.0              viridis_0.6.2             
[15] viridisLite_0.4.0          pastecs_1.3.21            
[17] kableExtra_1.3.4           lubridate_1.8.0           
[19] rsample_0.1.1              ggthemes_4.2.4            
[21] ggrepel_0.9.1              here_1.0.1                
[23] fs_1.5.0                   forcats_0.5.1             
[25] stringr_1.4.0              dplyr_1.0.7               
[27] purrr_0.3.4                readr_2.1.0               
[29] tidyr_1.1.4                tibble_3.1.6              
[31] ggplot2_3.3.5              tidyverse_1.3.1           
[33] knitr_1.36                

loaded via a namespace (and not attached):
 [1] colorspace_2.0-2  ellipsis_0.3.2    rprojroot_2.0.2  
 [4] rstudioapi_0.13   listenv_0.8.0     furrr_0.2.3      
 [7] farver_2.1.0      fansi_0.5.0       xml2_1.3.2       
[10] codetools_0.2-18  splines_4.1.2     jsonlite_1.7.2   
[13] broom_0.7.10      dbplyr_2.1.1      compiler_4.1.2   
[16] httr_1.4.2        backports_1.4.0   assertthat_0.2.1 
[19] fastmap_1.1.0     lazyeval_0.2.2    cli_3.1.0        
[22] htmltools_0.5.2   tools_4.1.2       gtable_0.3.0     
[25] glue_1.5.0        Rcpp_1.0.7        cellranger_1.1.0 
[28] jquerylib_0.1.4   vctrs_0.3.8       svglite_2.0.0    
[31] nlme_3.1-153      iterators_1.0.13  crosstalk_1.2.0  
[34] xfun_0.28         globals_0.14.0    rvest_1.0.2      
[37] lifecycle_1.0.1   future_1.23.0     hms_1.1.1        
[40] parallel_4.1.2    yaml_2.2.1        curl_4.3.2       
[43] gridExtra_2.3     sass_0.4.0        stringi_1.7.5    
[46] highr_0.9         foreach_1.5.1     boot_1.3-28      
[49] shape_1.4.6       rlang_0.4.12      pkgconfig_2.0.3  
[52] systemfonts_1.0.3 evaluate_0.14     lattice_0.20-45  
[55] htmlwidgets_1.5.4 labeling_0.4.2    tidyselect_1.1.1 
[58] parallelly_1.29.0 plyr_1.8.6        magrittr_2.0.1   
[61] R6_2.5.1          generics_0.1.1    DBI_1.1.1        
[64] pillar_1.6.4      haven_2.4.3       withr_2.4.2      
[67] mgcv_1.8-38       survival_3.2-13   modelr_0.1.8     
[70] crayon_1.4.2      Quandl_2.11.0     utf8_1.2.2       
[73] tzdb_0.2.0        rmarkdown_2.11    grid_4.1.2       
[76] readxl_1.3.1      data.table_1.14.2 reprex_2.0.1     
[79] digest_0.6.28     webshot_0.5.2     munsell_0.5.0    
[82] bslib_0.3.1       quadprog_1.5-8   
getwd()
[1] "/Users/ryanradcliff/Documents/BUS696/BROCODE_Final_Project"

# load all your libraries in this chunk 
library('tidyverse')
library("fs")
library('here')
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')
library('ggthemes')
library('kableExtra')
library('pastecs')
library('viridis')
library('plotly')
library('tidyquant')
library('scales')
library("gdata")
gdata: read.xls support for 'XLS' (Excel 97-2004) files
gdata: ENABLED.

gdata: read.xls support for 'XLSX' (Excel 2007+) files
gdata: ENABLED.

Attaching package: ‘gdata’

The following objects are masked from ‘package:xts’:

    first, last

The following objects are masked from ‘package:pastecs’:

    first, last

The following objects are masked from ‘package:dplyr’:

    combine, first, last

The following object is masked from ‘package:purrr’:

    keep

The following object is masked from ‘package:stats’:

    nobs

The following object is masked from ‘package:utils’:

    object.size

The following object is masked from ‘package:base’:

    startsWith
# note, do not run install.packages() inside a code chunk. install them in the console outside of a code chunk. 

Part 1 - Final Project Cleaning and Summary Statistics

1a) Loading data


#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space 
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

1b) Fixing the wonky DOB & Data cleanup


#(Birthdate and Age, ID as a number)adding DOB (Age/Spouse Age) in years columns and adding two fields for assignment and number of children and number of degrees
dataclean <- data %>%
  mutate(Birthdate = ifelse(Birthdate == "0001-01-01", NA, Birthdate)) %>%
  mutate(Birthdate = mdy(Birthdate)) %>%
  mutate(Age = as.numeric(floor(interval(start= Birthdate, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(Spouse.Birthdate = ifelse(Spouse.Birthdate == "0001-01-01", NA, Spouse.Birthdate)) %>%
  mutate(Spouse.Birthdate = mdy(Spouse.Birthdate)) %>%
  mutate(Spouse.Age = as.numeric(floor(interval(start= Spouse.Birthdate,
                                                end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(ID = as.numeric(ID)) %>% 
  mutate(Assignment_flag = ifelse(is.na(Assignment.Number), 0,1)) %>% 
  mutate( No_of_Children = ifelse(is.na(Child.1.ID),0,
                            ifelse(is.na(Child.2.ID),1,2)))%>%
 mutate(ID = as.numeric(ID)) %>% 
    mutate( nmb_degree = ifelse(is.na(Degree.Type.1),0,
                            ifelse(is.na(Degree.Type.2),1,2))) %>%
#conferral dates
  mutate(Conferral.Date.1 = ifelse(Conferral.Date.1 == "0001-01-01", NA, Conferral.Date.1)) %>%
  mutate(Conferral.Date.1 = mdy(Conferral.Date.1)) %>%
  mutate(Conferral.Date.1.Age = as.numeric(floor(interval(start= Conferral.Date.1, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Conferral.Date.2 = ifelse(Conferral.Date.2 == "0001-01-01", NA, Conferral.Date.2)) %>%
  mutate(Conferral.Date.2 = mdy(Conferral.Date.2)) %>%
  mutate(Conferral.Date.2.Age = as.numeric(floor(interval(start= Conferral.Date.2, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Last.Contact.By.Anyone = ifelse(Last.Contact.By.Anyone == "0001-01-01", NA, Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.By.Anyone = mdy(Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.Age = as.numeric(floor(interval(start= Last.Contact.By.Anyone, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
 mutate(HH.First.Gift.Date = ifelse(HH.First.Gift.Date == "0001-01-01", NA, HH.First.Gift.Date)) %>%
  mutate(HH.First.Gift.Date = mdy(HH.First.Gift.Date)) %>%
mutate(HH.First.Gift.Age = as.numeric(floor(interval(start= HH.First.Gift.Date, end=Sys.Date())/duration(n=1, unit="years")))) %>% 
#major gift 
  mutate(major_gifter = ifelse(Lifetime.Giving > 50000, 1,0) %>% factor(., levels = c("0","1"))) %>%
#splitting up the age into ranges and creating category for easy visualization 
  mutate(age_range = 
    ifelse(Age %in% 10:19, "10 < 20 years old",
    ifelse(Age %in% 20:29, "20 < 30 years old", 
    ifelse(Age %in% 30:39, "30 < 40 years old",
    ifelse(Age %in% 40:49, "40 < 50 years old",
    ifelse(Age %in% 50:59, "50 < 60 years old",
    ifelse(Age %in% 60:69, "60 < 70 years old",
    ifelse(Age %in% 70:79, "70 < 80 years old",
    ifelse(Age %in% 80:89, "80 < 90 years old",
    ifelse(Age %in% 90:120, "90+ years old",
    NA)))))))))) %>%
#creating a region column using the county data and the OMB MSA (Metropolitan Statistical Area) definitions
  mutate(region = 
    ifelse(County == "San Luis Obispo" & State == "CA", "So Cal",
    ifelse(County == "Kern" & State == "CA", "So Cal",
    ifelse(County == "San Bernardino" & State == "CA", "So Cal",
    ifelse(County == "Santa Barbara" & State == "CA", "So Cal",
    ifelse(County == "Ventura" & State == "CA", "So Cal",
    ifelse(County == "Los Angeles" & State == "CA", "So Cal",
    ifelse(County == "Orange" & State == "CA", "So Cal",
    ifelse(County == "Riverside" & State == "CA", "So Cal",
    ifelse(County == "San Diego" & State == "CA", "So Cal",
    ifelse(County == "Imperial" & State == "CA", "So Cal",
    ifelse(County == "King" & State == "WA", "Seattle",
    ifelse(County == "Snohomish" & State == "WA", "Seattle",
    ifelse(County == "Pierce" & State == "WA", "Seattle",
    ifelse(County == "Clackamas" & State == "OR", "Portland",
    ifelse(County == "Columbia" & State == "OR", "Portland",
    ifelse(County == "Multnomah" & State == "OR", "Portland",
    ifelse(County == "Washington" & State == "OR", "Portland",
    ifelse(County == "Yamhill" & State == "OR", "Portland",
    ifelse(County == "Clark" & State == "WA", "Portland",
    ifelse(County == "Skamania" & State == "WA", "Portland",
    ifelse(County == "Denver" & State == "CO", "Denver",
    ifelse(County == "Arapahoe" & State == "CO", "Denver",
    ifelse(County == "Jefferson" & State == "CO", "Denver",
    ifelse(County == "Adams" & State == "CO", "Denver",
    ifelse(County == "Douglas" & State == "CO", "Denver",
    ifelse(County == "Broomfield" & State == "CO", "Denver",    
    ifelse(County == "Elbert" & State == "CO", "Denver",
    ifelse(County == "Park" & State == "CO", "Denver",
    ifelse(County == "Clear Creek" & State == "CO", "Denver",
    ifelse(County == "Alameda" & State == "CA", "Bay Area",
    ifelse(County == "Contra Costa" & State == "CA", "Bay Area",
    ifelse(County == "Marin" & State == "CA", "Bay Area",
    ifelse(County == "Monterey" & State == "CA", "Bay Area",
    ifelse(County == "Napa" & State == "CA", "Bay Area",
    ifelse(County == "San Benito" & State == "CA", "Bay Area",
    ifelse(County == "San Francisco" & State == "CA", "Bay Area",
    ifelse(County == "San Mateo" & State == "CA", "Bay Area",
    ifelse(County == "Santa Clara" & State == "CA", "Bay Area",
    ifelse(County == "Santa Cruz" & State == "CA", "Bay Area",
    ifelse(County == "Solano" & State == "CA", "Bay Area",
    ifelse(County == "Sonoma" & State == "CA", "Bay Area",
           NA)))))))))))))))))))))))))))))))))))))))))) %>%
  mutate(region = 
    ifelse(County == "Kings" & State == "NY", "New York",
    ifelse(County == "Queens" & State == "NY", "New York",
    ifelse(County == "New York" & State == "NY", "New York",
    ifelse(County == "Bronx" & State == "NY", "New York",
    ifelse(County == "Richmond" & State == "NY", "New York",
    ifelse(County == "Westchester" & State == "NY", "New York",
    ifelse(County == "Bergen" & State == "NY", "New York",
    ifelse(County == "Hudson" & State == "NY", "New York",
    ifelse(County == "Passaic" & State == "NY", "New York",
    ifelse(County == "Putnam" & State == "NY", "New York",
    ifelse(County == "Rockland" & State == "NY", "New York",
    ifelse(County == "Suffolk" & State == "NY", "New York",
    ifelse(County == "Nassau" & State == "NY", "New York",
    ifelse(County == "Middlesex" & State == "NJ", "New York",
    ifelse(County == "Monmouth" & State == "NJ", "New York",
    ifelse(County == "Ocean" & State == "NJ", "New York",
    ifelse(County == "Somerset" & State == "NJ", "New York",
    ifelse(County == "Essex" & State == "NJ", "New York",
    ifelse(County == "Union" & State == "NJ", "New York",
    ifelse(County == "Morris" & State == "NJ", "New York",
    ifelse(County == "Sussex" & State == "NJ", "New York",
    ifelse(County == "Hunterdon" & State == "NJ", "New York",
    ifelse(County == "Pike" & State == "NJ", "New York",
    region)))))))))))))))))))))))) %>%

# code nor cal region as all others in CA not already defined
  mutate(region = 
    ifelse(State == "CA" & is.na(region) == TRUE, "Nor Cal", region))

#Initial Removal of Columns that provide no benefit 

dataclean <- subset(dataclean,select = -c(Assignment.Number
                                                    ,Assignment.has.Historical.Mngr
                                                    ,Suffix
                                                    ,Assignment.Date
                                                    ,Assignment.Manager
                                                    ,Assignment.Role
                                                    ,Assignment.Title
                                                    ,Assignment.Status
                                                    ,Strategy
                                                    ,Progress.Level
                                                    ,Assignment.Group
                                                    ,Assignment.Category
                                                    ,Funding.Method
                                                        ,Expected.Book.Date
                                                        ,Qualification.Amount
                                                        ,Expected.Book.Amount
                                                        ,Expected.Book.Date
                                                        ,Hard.Gift.Total
                                                        ,Soft.Credit.Total
                                                        ,Total.Assignment.Gifts
                                                        ,No.of.Pledges
                                                        ,Proposal..
                                                        ,Proposal.Notes
                                                        ,HH.Life.Spouse.Credit
                                                        ,Last.Contact.By.Manager
                                                        ,X..of.Contacts.By.Manager
                                                        ,DonorSearch.Range
                                                        ,iWave.Range
                                                        ,WealthEngine.Range
                                                        ,Philanthropic.Commitments
                                                        ))
#cleaning up zip codes removing -4 after 
dataclean$Zip <- gsub(dataclean$Zip, pattern="-.*", replacement = "")

#adding zip code data and column 
zip <- read.csv(here::here("final_project", "Salary_Zipcode.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")


#adding zip salary column
dataclean <-dataclean %>%
    mutate(zipcode_slry = VLOOKUP(Zip, zip, NAME, S1902_C03_002E)) %>%
#slry range 
  mutate(zipslry_range = 
    ifelse(zipcode_slry %in% 10000:89999, "90K-99K",
    ifelse(zipcode_slry %in% 90000:99999, "90K-99K",
    ifelse(zipcode_slry %in% 100000:149999, "100K-149K", 
    ifelse(zipcode_slry %in% 150000:199999, "150K-199K",
    ifelse(zipcode_slry %in% 200000:249999, "200K-249K",
    ifelse(zipcode_slry %in% 250000:299999, "250K-299K",
    ifelse(zipcode_slry %in% 300000:349999, "300K-349K",
    ifelse(zipcode_slry %in% 350000:399999, "350K-399K",
    ifelse(zipcode_slry %in% 400000:499999, "400K-499K",
    ifelse(zipcode_slry %in% 500000:999999, "500K-999K",
    NA)))))))))))

sum(is.na(dataclean$zipcode_slry))
[1] 62347
#adding scholarship data (y/n)
schlr <- read.csv(here::here("final_project", "scholarship.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

#adding scholarship column
dataclean <-dataclean %>%
    mutate(scholarship = VLOOKUP(ID, schlr, ID, SCHOLARSHIP)) 

#replacing NA with 0 
 dataclean$scholarship <- replace_na(dataclean$scholarship,'0')
 
#replacing Y with 1 
dataclean$scholarship<-ifelse(dataclean$scholarship=="Y",1,0)

#checking how many are N
table(dataclean$scholarship)

     0      1 
295264  27962 
#checking and deleting scholarship column 
class(dataclean$schlr_fct)
[1] "NULL"
dataclean = subset(dataclean, select = -c(scholarship))
  
#checking for duplicates N >1 indicates a records values are in the file twice 
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))

#removing duplicated records
dataclean <- unique(dataclean)

#Verifying n = 1 no ID with multiple records cleaned of dupes
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))
NA

1d Creating many many factor variables


dataclean <- 
  dataclean %>% 
  #SEX
  mutate(sex_fct = 
           fct_explicit_na(Sex),
sex_simple = 
    fct_lump_n(Sex, n = 4),
#MARRIED
married_fct = 
           fct_explicit_na(Married),
  #DONOR SEGMENT
  donorseg_fct = 
           fct_explicit_na(Donor.Segment),
         donorseg_simple = 
           fct_lump_n(Donor.Segment, n = 4),
  #CONTACT RULE
         contact_fct = 
           fct_explicit_na(Contact.Rules),
         contact_simple = 
           fct_lump_n(Contact.Rules, n = 4),
  #SPOUSE MAIL
         spomail_fct = 
           fct_explicit_na(Spouse.Mail.Rules),
         spomail_simple = 
           fct_lump_n(Spouse.Mail.Rules, n = 4),
  #JOB TITLE
         jobtitle_fct = 
           fct_explicit_na(Job.Title),
         jobtitle_simple = 
           fct_lump_n(Job.Title, n = 5),
  #DEGREE TYPE 1
         deg1_fct = 
           fct_explicit_na(Degree.Type.1),
         deg1_simple = 
           fct_lump_n(Degree.Type.1, n = 5),
  #DEGREE TYPE 2
         deg2_fct = 
           fct_explicit_na(Degree.Type.2),
         deg2_simple = 
           fct_lump_n(Degree.Type.2, n = 5),
  #MAJOR 1
         maj1_fct = 
           fct_explicit_na(Major.1),
         maj1_simple = 
           fct_lump_n(Major.1, n = 5),
  #MAJOR 2
         maj2_fct = 
           fct_explicit_na(Major.2),
         maj2_simple = 
           fct_lump_n(Major.2, n = 5),
  #MINOR 1
         min1_fct = 
           fct_explicit_na(Minor.1),
         min1_simple = 
           fct_lump_n(Minor.1, n = 5),
  #MINOR 2
         min2_fct = 
           fct_explicit_na(Minor.2),
         min2_simple = 
           fct_lump_n(Minor.2, n = 5),
  #SCHOOL 1
         school1_fct = 
           fct_explicit_na(School.1),
         school1_simple = 
           fct_lump_n(School.1, n = 5),
  #SCHOOL 2
         school2_fct = 
           fct_explicit_na(School.2),
         school2_simple = 
           fct_lump_n(School.2, n = 5),
  #INSTITUTION TYPE
         insttype_fct = 
           fct_explicit_na(Institution.Type),
         insttype_simple = 
           fct_lump_n(Institution.Type, n = 5),
  #EXTRACURRICULAR
         extra_fct = 
           fct_explicit_na(Extracurricular),
         extra_simple = 
           fct_lump_n(Extracurricular, n = 5),
  #HH FIRST GIFT FUND
         hhfirstgift_fct = 
           fct_explicit_na(HH.First.Gift.Fund),
         hhfirstgift_simple = 
           fct_lump_n(HH.First.Gift.Fund, n = 5),
#CHILD 1 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.1.Enroll.Status),
         ch1_enroll_simple = 
           fct_lump_n(Child.1.Enroll.Status, n = 4),
#CHILD 1 MAJOR
         ch1_maj_fct = 
           fct_explicit_na(Child.1.Major),
         ch1_maj_simple = 
           fct_lump_n(Child.1.Major, n = 4),
#CHILD 1 MINOR
         ch1_min_fct = 
           fct_explicit_na(Child.1.Minor),
         ch1_min_simple = 
           fct_lump_n(Child.1.Minor, n = 4),
#CHILD 1 SCHOOL
         ch1_school_fct = 
           fct_explicit_na(Child.1.School),
         ch1_school_simple = 
           fct_lump_n(Child.1.School, n = 4),
#CHILD 1 FEEDER
         ch1_feeder_fct = 
           fct_explicit_na(Child.1.Feeder.School),
         ch1_feeder_simple = 
           fct_lump_n(Child.1.Feeder.School, n = 4),
#CHILD 2 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.2.Enroll.Status),
         ch2_enroll_simple = 
           fct_lump_n(Child.2.Enroll.Status, n = 4),
#CHILD 2 MAJOR
         ch2_maj_fct = 
           fct_explicit_na(Child.2.Major),
         ch2_maj_simple = 
           fct_lump_n(Child.2.Major, n = 4),
#CHILD 2 MINOR
         ch2_min_fct = 
           fct_explicit_na(Child.2.Minor),
         ch2_min_simple = 
           fct_lump_n(Child.2.Minor, n = 4),
#CHILD 2 SCHOOL
         ch2_school_fct = 
           fct_explicit_na(Child.2.School),
         ch2_school_simple = 
           fct_lump_n(Child.2.School, n = 4),
#CHILD 2 FEEDER
         ch2_feeder_fct = 
           fct_explicit_na(Child.2.Feeder.School),
         ch2_feeder_simple = 
           fct_lump_n(Child.2.Feeder.School, n = 4),
    )



#checking to see if its a factor
#class(dataclean$sex_fct)
#class(dataclean$donorseg_fct)
#class(dataclean$contact_fct)
#class(dataclean$spomail_fct)

#checking levels
#levels(dataclean$sex_simple)
#levels(dataclean$donorseg_simple)
#levels(dataclean$contact_simple)
#levels(dataclean$spomail_simple)
#levels(dataclean$hhfirstgift_simple)

#creating a table against Sex column 
#table(dataclean$sex_fct, dataclean$sex_simple)

Region Analysis

#grouping by region and analyzing 
dataclean %>%
  group_by(region) %>%
  summarise(Count = length(region),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  mutate(mean_total_giv = dollar(mean_total_giv)) %>%
  kable(col.names = c("Region", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Region Count Mean HH Lifetime Giving
So Cal 145139 $5,090.84
NA 130306 $2,040.98
Bay Area 20641 $755.92
Nor Cal 10707 $3,823.63
Seattle 5425 $922.08
New York 4959 $1,978.49
Portland 2976 $1,098.24
Denver 2847 $257.29
NA
NA

DonorSegment Analysis

#grouping by donorsegment and analyzing 
dataclean %>%
  group_by(Donor.Segment) %>%
  summarise(Count = length(Donor.Segment),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  #added scales package to have the values show in dollar 
  mutate(mean_total_giv = (dollar(mean_total_giv))) %>%
  kable(col.names = c("Donor Segment", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Donor Segment Count Mean HH Lifetime Giving
NA 231974 $0.00
Lost Donor 69718 $4,954.47
Lapsed Donor 11193 $10,069.79
Current Donor 5603 $90,638.32
Lapsing Donor 3862 $16,590.15
At-Risk Donor 650 $77,143.93
NA
NA

First gift size

aq <- quantile(dataclean$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

aq <- as.data.frame(aq)

aq$aq <- dollar(aq$aq)

aq %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% $0.00
50% $0.00
75% $0.00
90% $40.00
99% $1,910.06
NA
NA

Consecutive giving

#consecutive years of giving 
dataclean %>%
  filter(Max.Consec.Fiscal.Years > 0) %>%
  ggplot(aes(Max.Consec.Fiscal.Years)) + geom_histogram(fill = "#002845", bins = 20) + 
  theme_economist_white() +
  ggtitle("Consecutive Years of Giving Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,2)) +
  scale_y_continuous(breaks = seq(0,10000000,5000)) 

NA
NA
NA

Lifetime giving based on number of children

dataclean %>%
  filter(HH.Lifetime.Giving <= 10000) %>%
  filter(HH.Lifetime.Giving > 0) %>%
  mutate(`No_of_Children` = as.factor(`No_of_Children`)) %>%
  ggplot(aes(HH.Lifetime.Giving, fill = `No_of_Children`)) + geom_histogram(bins = 30) + theme_economist_white() +
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,100000,1000)) +
  scale_y_continuous(breaks = seq(0,100000000,5000)) +
  ggtitle("Giving distribution and number of children")+ 
  scale_fill_manual(values=c("#002845", "#00cfcc", "#ff9973"))

NA
NA
NA

Mean, Median, and Count of Giving in Age Ranges


age_range_giving <- dataclean %>%
  group_by(age_range) %>%
  summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
            med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
            amount_of_people_in_age_range = n())


glimpse(age_range_giving)
Rows: 10
Columns: 4
$ age_range                     <chr> "10 < 20 years old", "20 < 30 …
$ avg_giving                    <dbl> 0.4464244, 28.2686916, 386.687…
$ med_giving                    <dbl> 0, 0, 0, 0, 0, 0, 0, 10, 15, 0
$ amount_of_people_in_age_range <int> 3977, 24556, 21022, 16834, 207…

Part 2 - Summary Statistics

2a) Plotting average giving by age range


age_range_giving <-
  age_range_giving %>%
  mutate(age_range = factor(age_range))

ggplot(age_range_giving, aes(age_range, avg_giving)) +
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + labs(x = "Age Range", y = "Average Giving") +
      ggtitle("Average Giving Compared Across Age Ranges")

NA
NA

2b) Count of donors based on age range (another way to look at it)


ggplot(dataclean, 
       aes(age_range)) + 
       geom_bar() + 
       theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + 
  labs(title = "Count of Age Ranges", x = "", y = "")

NA
NA

2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.


ggplot(dataclean, aes(age_range,HH.Lifetime.Giving,fill = age_range)) + 
  geom_boxplot(
  outlier.colour = "red") + 
  scale_y_log10() +
  theme(axis.text.x=element_text(angle=45,hjust=1))  + labs(x = "Age Range", y = "Lifetime Giving Amount") +
      ggtitle("Lifetime Giving Compared Across Age Ranges")

NA
NA

2d) Splitting by age and gender



#creating boxplots 
dataclean %>% 
  filter(Age < 100) %>% #removing the weird outliers that are over 100 
  filter(Sex %in% c("M", "F")) %>%
  ggplot(aes(Sex, Age)) + 
  geom_boxplot() + 
  theme_economist() + 
  ggtitle("Ages of Donors Based on Gender") + 
  xlab(NULL) + ylab(NULL)
  

Giving by gender


#remove NAs U X

dataclean2 <- dataclean %>%
  filter(Sex %in% c("M", "F")) 

q <- ggplot(dataclean2) 
q + stat_summary_bin(
  aes(y = HH.Lifetime.Giving, x = Sex), 
  fun.y = "mean", geom = "bar") 
  
summary(dataclean$sex_simple)

Mean age by gender

#breakdown of sexs 
tally(group_by(dataclean, Sex))

summarize(group_by(dataclean, Sex), 
          avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
          avg_age = mean(Age, na.rm = TRUE),
          med_age = median(Age, na.rm = TRUE))

#grouping by sex and age range for slides 
tally(group_by(dataclean, Sex, age_range))

2e) Distribution of people in the states that they live.


  dataclean %>%
  mutate(State = ifelse(State == " ", "NA", State)) %>%
  filter(State != "NA") %>%
  group_by(State) %>%
  summarise(Count = length(State)) %>%
  filter(Count > 800) %>%
  arrange(-Count) %>%
  kable(col.names = c("Donor's State", "Count")) %>%
  kable_styling(bootstrap_options = c("condensed"),
                full_width = F)
Donor's State Count
CA 176487
WA 7957
TX 7266
NY 5659
CO 5073
AZ 4925
OR 4612
FL 4111
IL 3681
HI 3394
PA 2904
OH 2754
NV 2715
MI 2523
MA 2473
NJ 2311
VA 2158
NC 2085
GA 2044
MO 1889
MN 1732
MD 1488
TN 1443
IN 1417
CT 1380
WI 1330
UT 1173
OK 1151
AL 1120
LA 1110
ID 1096
SC 1076
KY 1032
KS 1027
NM 981
IA 880
NA
NA

2f) Looking at all donors first gift amount. 75% made a first gift of <100.


 no_non_donors <- dataclean %>%
  filter(Lifetime.Giving != 0)
  
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

nd <- as.data.frame(nd)

nd %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
  
  

Part 3 - Modeling

Split data and create a new set for easier analysis


#converting married Y and N to 1 and 0 
dataclean <- dataclean %>%
      mutate(Married_simple = ifelse(Married == "N",0,1))


dataclean <- dataclean %>%
  mutate(hh.lifetime.giving_fct = as.factor(HH.Lifetime.Giving)) %>%
  mutate(HH.Lifetime.Giving.Plus = log(HH.Lifetime.Giving + 1))



#Creating the velvet set - only the best can enter

datavelvet <- subset(dataclean,select = -c(
  age_range,
  Athletics,
  Birthdate,
  Category.Codes,
  Category.Descriptions,
  Child.1.Enroll.Status,
  Child.1.ID,
  Child.2.ID,
  Child.1.Major,
  Child.1.Minor,
  Child.1.School,
  Child.1.Feeder.School,
  Child.2.Enroll.Status,
  Child.2.Major,
  Child.2.Minor,
  Child.2.School,
  Child.2.Feeder.School,
  ch1_enroll_fct,
  ch1_maj_fct,
  ch1_min_fct,
  ch1_school_fct,
  ch1_feeder_fct,
  ch2_school_fct,
  ch2_feeder_fct,
  ch2_maj_fct,
  ch2_min_fct,
  Contact.Rules,
  Degree.Type.1,
  Degree.Type.2,
  Donor.Segment,
  City,
  County,
  Conferral.Date.1,
  Conferral.Date.2,
  Contact.Rules,
  contact_fct,
  Degree.Type.1,
  Degree.Type.2,
  deg1_fct,
  deg2_fct,
  donorseg_fct,
  Extracurricular,
  extra_fct,
  HH.First.Gift.Fund,
  HH.First.Gift.Date,
  hhfirstgift_fct,
  hh.lifetime.giving_fct,
  ID,
  Institution.Type,
  insttype_fct,
  Job.Title,
  jobtitle_fct,
  Last.Contact.By.Anyone,
  LegacyLeader..compass.score.,
  major_gifter,
  Major.1,
  Major.2,
  Minor.1,
  Minor.2,
  maj1_fct,
  maj2_fct,
  min1_fct,
  min2_fct,
  region,
  zipcode_slry,
  Sex,
  Scholarship,
  School.1,
  School.2,
  school1_fct,
  school2_fct,
  Spouse.Birthdate,
  Spouse.Mail.Rules,
  spomail_fct,
  State,
  Zip,
  zipslry_range
))

#datavelvet <-
#datavelvet[sapply(datavelvet, is.character)] <- #lapply(datavelvet[sapply(datavelvet, is.character)], 
#                                                           as.factor)

library("rsample")

data_split <- initial_split(datavelvet, prop = 0.75)

data_train <- training(data_split)
data_test <- testing(data_split)
p <- datavelvet %>%
  ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
  scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))
Scale for 'x' is already present. Adding another scale for 'x',
which will replace the existing scale.
ggplotly(p)
  
p


ggplot(data = datavelvet, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

NA
NA
NA

Another Histogram


datavelvet %>%
  filter(Age >= 10) %>%
  filter(Age <= 90) %>%
  ggplot(aes(Age)) + geom_histogram(fill = "#002845", bins = 20) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,10000000,2000)) 

Age distribution by gender

#Age Gender filtered out below 15 and above 90 - also removed U X the weird values 
datavelvet %>%
  filter(Age >= 15) %>%
  filter(Age <= 90) %>%
  mutate(Sex = as.factor(Sex)) %>%
  filter(Sex != "U") %>%
  filter(Sex != "X") %>%
  ggplot(aes(Age, fill = Sex)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Age Distribution by Gender") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,10)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Donor age distribution by marital status

#Age Marital Status
datavelvet %>%
  filter(Age >= 20) %>%
  filter(Age <= 85) %>%
  ggplot(aes(Age, fill = Married)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution by Marital Status") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Linear Model

#These will focus on predicting whether a constituent is a donor or non-donor. 


mod1lm <- lm( Lifetime.Giving ~ Married_simple,
           data = data_train)

mod2lm <- lm( Total.Giving.Years ~ Lifetime.Giving,
           data = data_train)

mod3lm <- lm( Lifetime.Giving ~ region,
           data = data_train)

summary(mod1lm)

Call:
lm(formula = Lifetime.Giving ~ Married_simple, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -2867    -2742    -2661    -2661 18111464 

Coefficients:
               Estimate Std. Error t value            Pr(>|t|)    
(Intercept)      2660.9      251.3  10.588 <0.0000000000000002 ***
Married_simple    205.9      469.1   0.439               0.661    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 104400 on 242248 degrees of freedom
Multiple R-squared:  7.953e-07, Adjusted R-squared:  -3.333e-06 
F-statistic: 0.1927 on 1 and 242248 DF,  p-value: 0.6607
summary(mod2lm)

Call:
lm(formula = Total.Giving.Years ~ Lifetime.Giving, data = data_train)

Residuals:
    Min      1Q  Median      3Q     Max 
-36.600  -0.554  -0.554  -0.554  39.403 

Coefficients:
                     Estimate    Std. Error t value
(Intercept)     0.55445026328 0.00396511550  139.83
Lifetime.Giving 0.00000343205 0.00000003795   90.43
                           Pr(>|t|)    
(Intercept)     <0.0000000000000002 ***
Lifetime.Giving <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.951 on 242248 degrees of freedom
Multiple R-squared:  0.03266,   Adjusted R-squared:  0.03265 
F-statistic:  8178 on 1 and 242248 DF,  p-value: < 0.00000000000000022
summary(mod3lm)

Call:
lm(formula = Lifetime.Giving ~ region, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -3977    -3968    -3968    -3598 18110156 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)       513.0      950.9   0.539 0.589558    
regionDenver     -367.7     2739.3  -0.134 0.893220    
regionNew York   1954.2     2160.7   0.904 0.365769    
regionNor Cal    3464.0     1623.1   2.134 0.032826 *  
regionPortland    161.0     2680.2   0.060 0.952111    
regionSeattle    -128.2     2088.2  -0.061 0.951057    
regionSo Cal     3455.5     1016.1   3.401 0.000672 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 118200 on 144684 degrees of freedom
  (97559 observations deleted due to missingness)
Multiple R-squared:  0.0001214, Adjusted R-squared:  7.989e-05 
F-statistic: 2.927 on 6 and 144684 DF,  p-value: 0.007435
#increasing the giving year one year increase total giving by 0.0035


ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~region) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Region")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~nmb_degree) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Number of Degrees")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.First.Gift.Amount))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~donorseg_fct) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Donor Segment")
`geom_smooth()` using formula 'y ~ x'

#This plot actually has some interesting results
ggplot(data = data_train, aes(x = Age, y = log(Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~No_of_Children) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("# Children")
`geom_smooth()` using formula 'y ~ x'

data_train %>% 
  select_if(is.factor) %>% 
  glimpse()
Rows: 242,250
Columns: 54
$ major_gifter           <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ sex_fct                <fct> F, M, F, M, (Missing), M, M, (Missing…
$ sex_simple             <fct> F, M, F, M, NA, M, M, NA, M, NA, M, M…
$ married_fct            <fct> Y, Y, N, N, N, N, N, N, N, N, N, N, N…
$ donorseg_fct           <fct> Lost Donor, (Missing), (Missing), (Mi…
$ donorseg_simple        <fct> Lost Donor, NA, NA, NA, NA, Lost Dono…
$ contact_fct            <fct> No Solicitations, (Missing), (Missing…
$ contact_simple         <fct> No Solicitations, NA, NA, NA, NA, No …
$ spomail_fct            <fct> No Solicitations, (Missing), (Missing…
$ spomail_simple         <fct> No Solicitations, NA, NA, NA, NA, NA,…
$ jobtitle_fct           <fct> (Missing), Manager, (Missing), Public…
$ jobtitle_simple        <fct> NA, Other, NA, Other, NA, NA, NA, NA,…
$ deg1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg1_simple            <fct> NA, NA, NA, NA, NA, Bachelor of Arts,…
$ deg2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg2_simple            <fct> NA, NA, NA, NA, NA, Master of Arts, N…
$ maj1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj1_simple            <fct> NA, NA, NA, NA, NA, Other, Law (Full-…
$ maj2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj2_simple            <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ min1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min1_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ min2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min2_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ school1_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school1_simple         <fct> NA, NA, NA, NA, NA, NA, Other, NA, NA…
$ school2_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school2_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ insttype_fct           <fct> (Missing), (Missing), (Missing), (Mis…
$ insttype_simple        <fct> NA, NA, NA, NA, NA, NA, Law JD Full-T…
$ extra_fct              <fct> (Missing), (Missing), (Missing), (Mis…
$ extra_simple           <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ hhfirstgift_fct        <fct> (Missing), (Missing), (Missing), (Mis…
$ hhfirstgift_simple     <fct> NA, NA, NA, NA, NA, Pre-SRN Conversio…
$ ch1_enroll_fct         <fct> (Missing), Program Completed, (Missin…
$ ch1_enroll_simple      <fct> NA, NA, NA, NA, NA, Program Completed…
$ ch1_maj_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_maj_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_min_simple         <fct> NA, NA, NA, NA, NA, Non-Degree: GR Ta…
$ ch1_school_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_school_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_feeder_fct         <fct> (Missing), Palm Beach State College, …
$ ch1_feeder_simple      <fct> NA, Other, NA, NA, NA, NA, NA, NA, NA…
$ ch2_enroll_simple      <fct> NA, Program Completed, NA, NA, NA, NA…
$ ch2_maj_fct            <fct> (Missing), Business Administration BS…
$ ch2_maj_simple         <fct> NA, Business Administration BS, NA, N…
$ ch2_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_min_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch2_school_fct         <fct> (Missing), George L. Argyros School o…
$ ch2_school_simple      <fct> NA, George L. Argyros School of Busin…
$ ch2_feeder_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_feeder_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ hh.lifetime.giving_fct <fct> 25, 0, 0, 0, 0, 8048.75, 0, 0, 0, 0, …

MORE MODELS

Big logistic model


# Set family to binomial to set logistic function
# Run the model on the training set

donor_logit1 <-
  glm(hh.lifetime.giving_fct ~ Married_simple,
      family = "binomial",
      data = data_train)

summary(donor_logit1)


donor_logit2 <-
  glm(hh.lifetime.giving_fct ~ No_of_Children,
      family = "binomial",
      data = data_train)

summary(donor_logit2)


#summary(data_train$major_gifter)
#Assignment_flag taken out - may add back

donor_logit3 <-
  glm(major_gifter ~ Married + No_of_Children + donorseg_simple +  Total.Giving.Years + nmb_degree,
      family = "binomial",
      data = data_train)

summary(donor_logit3)

Call:
glm(formula = major_gifter ~ Married + No_of_Children + donorseg_simple + 
    Total.Giving.Years + nmb_degree, family = "binomial", data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.0162  -0.1346  -0.1189  -0.0647   4.2151  

Coefficients:
                              Estimate Std. Error z value
(Intercept)                  -3.623014   0.232406 -15.589
MarriedY                     -1.196678   0.088444 -13.530
No_of_Children                0.610558   0.060093  10.160
donorseg_simpleCurrent Donor -0.086079   0.238167  -0.361
donorseg_simpleLapsed Donor  -0.685781   0.244285  -2.807
donorseg_simpleLapsing Donor -0.491440   0.259772  -1.892
donorseg_simpleLost Donor    -1.349829   0.232121  -5.815
Total.Giving.Years            0.206177   0.005601  36.809
nmb_degree                   -2.493578   0.146982 -16.965
                                         Pr(>|z|)    
(Intercept)                  < 0.0000000000000002 ***
MarriedY                     < 0.0000000000000002 ***
No_of_Children               < 0.0000000000000002 ***
donorseg_simpleCurrent Donor               0.7178    
donorseg_simpleLapsed Donor                0.0050 ** 
donorseg_simpleLapsing Donor               0.0585 .  
donorseg_simpleLost Donor           0.00000000606 ***
Total.Giving.Years           < 0.0000000000000002 ***
nmb_degree                   < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9606.7  on 68301  degrees of freedom
Residual deviance: 7052.9  on 68293  degrees of freedom
  (173948 observations deleted due to missingness)
AIC: 7070.9

Number of Fisher Scoring iterations: 8
exp(donor_logit3$coefficients)
                 (Intercept)                     MarriedY 
                  0.02670207                   0.30219634 
              No_of_Children donorseg_simpleCurrent Donor 
                  1.84145937                   0.91752216 
 donorseg_simpleLapsed Donor donorseg_simpleLapsing Donor 
                  0.50369656                   0.61174479 
   donorseg_simpleLost Donor           Total.Giving.Years 
                  0.25928458                   1.22897028 
                  nmb_degree 
                  0.08261383 
#training predictions for in sample preds 
preds_train <- predict(donor_logit3, newdata = data_train, type = "response") 

#test predicts for OOS (out of sample)
preds_test <- predict(donor_logit3, newdata = data_test, type = "response")

head(preds_train)
     236259      260845      287321      219737       27673 
0.015426706 0.009114444          NA          NA          NA 
     298107 
         NA 
head(preds_test)
          4          10          11          14          15 
0.132366655 0.002564702 0.888420638 0.136046442 0.742195040 
         18 
0.002087869 
results_train <- data.frame(
  `truth` = data_train   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_train,
  `type` = rep("train",length(preds_train))
)

results_test <- data.frame(
  `truth` = data_test   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_test,
  `type` = rep("test",length(preds_test))
)

results <- bind_rows(results_train,results_test)

dim(results_train)
[1] 242250      3
dim(results_test)
[1] 80750     3
dim(results)
[1] 323000      3
library('plotROC')

p_plot <-
  ggplot(results,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 2.5,
           #Took the labelsize down to avoid cutoff
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 #We removed some of the cutoffs to avoid the mashup near the origin.

  #Changed the theme to avoid cutoff plot values.
  theme_classic(base_size = 14) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")
print(p_plot) 



p_train <-
  ggplot(results_train,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

p_test <-
  ggplot(results_test,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

#summary(donor_logit3)
#coef(donor_logit3)


#Calculating AUC of both
print(calc_auc(p_train)$AUC)
[1] 0.8918463
print(calc_auc(p_test)$AUC)
[1] 0.8839279

RIDGE


library('glmnet')
library('glmnetUtils')

ridge_fit1 <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + donorseg_fct + No_of_Children,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit1)
Call:
cv.glmnet.formula(formula = HH.Lifetime.Giving.Plus ~ sex_fct + 
    donorseg_fct + No_of_Children, data = data_train, alpha = 0)

Model fitting options:
    Sparse model matrix: FALSE
    Use model.frame: FALSE
    Number of crossvalidation folds: 10
    Alpha: 0
    Deviance-minimizing lambda: 0.2204993  (+1 SE): 0.2914871
print(ridge_fit1$lambda.min)
[1] 0.2204993
print(ridge_fit1$lambda.1se)
[1] 0.2914871
plot(ridge_fit1)

BIG RIDGE


library('glmnet')
library('glmnetUtils')

data_train %>% map(levels) %>% map(length)
$HH.Total.Gifts.FY20.21
[1] 0

$HH.Total.Gifts.FY19.20
[1] 0

$HH.Total.Gifts.FY18.19
[1] 0

$HH.Total.Gifts.FY17.18
[1] 0

$HH.Total.Gifts.FY16.17
[1] 0

$Class.Year
[1] 0

$Spouse.Class.Year
[1] 0

$Child.1.Class.Year
[1] 0

$Child.2.Class.Year
[1] 0

$Lifetime.Giving
[1] 0

$HH.Lifetime.Giving
[1] 0

$Total.Giving.Years
[1] 0

$Total.Giving.Fiscal.Years
[1] 0

$Max.Consec.Fiscal.Years
[1] 0

$HH.Life.Hard.Credit
[1] 0

$HH.Life.Soft.Credit
[1] 0

$HH.First.Gift.Amount
[1] 0

$Months.Since.Last.Gift
[1] 0

$Compass.Score
[1] 0

$Age
[1] 0

$Spouse.Age
[1] 0

$Assignment_flag
[1] 0

$No_of_Children
[1] 0

$nmb_degree
[1] 0

$Conferral.Date.1.Age
[1] 0

$Conferral.Date.2.Age
[1] 0

$Last.Contact.Age
[1] 0

$HH.First.Gift.Age
[1] 0

$sex_simple
[1] 4

$married_fct
[1] 2

$donorseg_simple
[1] 5

$contact_simple
[1] 5

$spomail_simple
[1] 5

$jobtitle_simple
[1] 6

$deg1_simple
[1] 6

$deg2_simple
[1] 6

$maj1_simple
[1] 6

$maj2_simple
[1] 6

$min1_simple
[1] 6

$min2_simple
[1] 7

$school1_simple
[1] 6

$school2_simple
[1] 6

$insttype_simple
[1] 6

$extra_simple
[1] 6

$hhfirstgift_simple
[1] 6

$ch1_enroll_simple
[1] 5

$ch1_maj_simple
[1] 5

$ch1_min_simple
[1] 5

$ch1_school_simple
[1] 5

$ch1_feeder_simple
[1] 5

$ch2_enroll_simple
[1] 5

$ch2_maj_simple
[1] 5

$ch2_min_simple
[1] 5

$ch2_school_simple
[1] 5

$ch2_feeder_simple
[1] 5

$Married_simple
[1] 0

$HH.Lifetime.Giving.Plus
[1] 0
ridge_fit2 <- cv.glmnet(HH.Lifetime.Giving.Plus ~ Age + school1_simple + insttype_simple + extra_simple + ch1_maj_simple + ch2_maj_simple  + Married_simple + donorseg_simple + married_fct + sex_simple + nmb_degree,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit2)
Call:
cv.glmnet.formula(formula = HH.Lifetime.Giving.Plus ~ Age + school1_simple + 
    insttype_simple + extra_simple + ch1_maj_simple + ch2_maj_simple + 
    Married_simple + donorseg_simple + married_fct + sex_simple + 
    nmb_degree, data = data_train, alpha = 0)

Model fitting options:
    Sparse model matrix: FALSE
    Use model.frame: FALSE
    Number of crossvalidation folds: 10
    Alpha: 0
    Deviance-minimizing lambda: 11.86158  (+1 SE): 1186.158
print(ridge_fit2$lambda.min)
[1] 11.86158
print(ridge_fit2$lambda.1se)
[1] 1186.158
plot(ridge_fit2)

LASSO


#Using cv.glmnet from class
#ls(data_train) 
#is.factor(data_train$major_gifter)
#glimpse(data_train$Lifetime.Giving)

#data_train %>% 
#  select_if(is.factor) %>% 
#  glimpse()



library(glmnet)
library(glmnetUtils)
lasso_fit <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       #Alpha 1 for lasso
                       alpha = 1)


print(lasso_fit$lambda.min)
[1] 0.01891898
#
print(lasso_fit$lambda.1se)
[1] 0.1009649
plot(lasso_fit)

LASSO 2



library(glmnet)
library(glmnetUtils)
lasso_fit <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + Age + school1_simple + insttype_simple + extra_simple + Married + donorseg_simple + nmb_degree+ No_of_Children,
                       data = data_train,
                       #Alpha 1 for lasso
                       alpha = 1)


print(lasso_fit$lambda.min)
[1] 0.004309598
#
print(lasso_fit$lambda.1se)
[1] 0.06399617
plot(lasso_fit)


coef(lasso_fit)
43 x 1 sparse Matrix of class "dgCMatrix"
                                                                                        s1
(Intercept)                                                                       6.822196
Age                                                                               .       
school1_simpleCollege of Health and Behavioral Sciences                           .       
school1_simpleDonna Ford Attallah College of Educational Studies                  .       
school1_simpleGeorge L. Argyros School of Business and Economics                  .       
school1_simpleLawrence and Kristina Dodge Coll of Film & Media                    .       
school1_simpleWilkinson Coll of Arts  Humanities  & Soc Sciences                  .       
school1_simpleOther                                                               .       
insttype_simpleGraduate Degree                                                    .       
insttype_simpleLaw JD Full-Time Program                                           .       
insttype_simpleUndergraduate Degree                                               .       
insttype_simpleUndergraduate Degree | Undergraduate Degree                        .       
insttype_simpleUndergraduate Degree | Undergraduate Degree | Undergraduate Degree .       
insttype_simpleOther                                                              .       
extra_simpleChapman Choir Tour                                                    .       
extra_simpleDisciples on Campus                                                   .       
extra_simpleFootball                                                              .       
extra_simpleInternational Student                                                 .       
extra_simpleWorld Campus Afloat/Sem at Sea                                        .       
extra_simpleOther                                                                 .       
ch1_maj_simpleBusiness Administration BS                                          .       
ch1_maj_simpleCommunication Studies BA                                            .       
ch1_maj_simpleFilm Production BFA                                                 .       
ch1_maj_simplePsychology BA                                                       .       
ch1_maj_simpleOther                                                               .       
ch2_maj_simpleBusiness Administration BS                                          .       
ch2_maj_simpleCommunication Studies BA                                            .       
ch2_maj_simpleFilm Production BFA                                                 .       
ch2_maj_simplePsychology BA                                                       .       
ch2_maj_simpleOther                                                               .       
Married_simple                                                                    .       
donorseg_simpleAt-Risk Donor                                                      .       
donorseg_simpleCurrent Donor                                                      .       
donorseg_simpleLapsed Donor                                                       .       
donorseg_simpleLapsing Donor                                                      .       
donorseg_simpleLost Donor                                                         .       
married_fctN                                                                      .       
married_fctY                                                                      .       
sex_simpleF                                                                       .       
sex_simpleM                                                                       .       
sex_simpleU                                                                       .       
sex_simpleX                                                                       .       
nmb_degree                                                                        .       
#Default setting is lambda.1se

#From the book - showing convergence with lambda values
plot(lasso_fit$glmnet.fit, xvar="lambda")

#abline(v=log(c(lasso_fit$lambda.min, lasso_fit$lambda.1se)), lty=2)

enet_mod <- cva.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       alpha = seq(0,1, by = 0.1))

print(enet_mod)
plot(enet_mod)

ELASTICNET


minlossplot(enet_mod, 
            cv.type = "min")

get_alpha <- function(fit) {
  alpha <- fit$alpha
  error <- sapply(fit$modlist, 
                  function(mod) {min(mod$cvm)})
  alpha[which.min(error)]
}

get_model_params <- function(fit) {
  alpha <- fit$alpha
  lambdaMin <- sapply(fit$modlist, `[[`, "lambda.min")
  lambdaSE <- sapply(fit$modlist, `[[`, "lambda.1se")
  error <- sapply(fit$modlist, function(mod) {min(mod$cvm)})
  best <- which.min(error)
  data.frame(alpha = alpha[best], lambdaMin = lambdaMin[best],
             lambdaSE = lambdaSE[best], eror = error[best])
}

best_alpha <- get_alpha(enet_mod)
print(best_alpha)
get_model_params(enet_mod)

best_mod <- enet_mod$modlist[[which(enet_mod$alpha == best_alpha)]]

print(best_mod)

minlossplot(enet_mod, cv.type = "min")

Ridges plot - could be useful for plotting donations vs donor segment


library('ggridges')

summary(data_train$variable)

ggplot(data_train, aes(x = HH.Lifetime.Giving, y = region)) + geom_density_ridges(rel_min_height = 0.005) + xlim(c(25000, 100000)) + 
      ggtitle("HH Lifetime Giving by Region")

library('corrplot')

#removing ID zip and nonnumeric 
corrplot_data <- dataclean[-c(1:48,52:56,58:60,63,66:67,70:72,74:81,83:132)]

#Convert from character to numeric data type
convert_fac2num <- function(x){
  as.numeric(as.factor(x))
}

corrplot_data <- mutate_at(corrplot_data,
                     .vars = c(1:12),
                     .funs = convert_fac2num)
#making a matrix
cd_cor <- cor(corrplot_data)

#creating correlation
col <- colorRampPalette(c("#BB4400", "#EE9990", 
  "#FFFFFF", "#77AAEE", "#4477BB"))
corrplot(cd_cor, method="color", col=col(100),
  type="lower", addCoef.col = "black",
  tl.pos="lt", tl.col="black", 
  tl.cex=0.7, tl.srt=45, 
  number.cex=0.7,
  diag=FALSE)

#correlation matrix
# pairs(~Age + Months.Since.Last.Gift + donorseg_fct + 
#     nmb_degree + No_of_Children + HH.First.Gift.Age + HH.First.Gift.Amount + Total.Giving.Years,
#     col = corrplot_data$HH.Lifetime.Giving,
#     data = corrplot_data, 
#     main = "Donor Scatter Plot Matrix")

#worthless.. 

ggplot(data = corrplot_data, aes(x = nmb_degree, y = HH.Lifetime.Giving)) + 
  geom_point(aplha = 1/10)+
  geom_smooth(method = "lm", color ="red") 

Random Forest


library('randomForest')

rf_fit_donor <- randomForest(Lifetime.Giving ~ ., 
                       data = data_train,
                       type = classification,
                       mtry = 7,
                       na.action = na.roughfix,
                       ntree = 200,
                       importance=TRUE
                       )

print(rf_fit_donor)

varImpPlot(rf_fit_donor, sort = TRUE, 
           n.var = 5,
           type = 2, class = NULL, scale = TRUE, 
           main = deparse(substitute(rf_fit_donor)))

library('randomForestExplainer')

plot_min_depth_distribution(
  rf_fit_donor,
  k = 10,
  min_no_of_trees = 0,
  mean_sample = "top_trees",
  mean_scale = FALSE,
  mean_round = 2,
  main = "Distribution of minimal depth and its mean"
)
#Splitting Category out to check if the category is useful for analysis
data_category_split_out <- dataclean %>%
  mutate(Category.Codes = trim(strsplit(as.character(Category.Codes), "|", fixed = TRUE))) %>%
  unnest(Category.Codes) %>% pivot_wider(names_from = Category.Codes,values_from =Category.Codes, values_fn = length)
LS0tCnRpdGxlOiAiQlJPQ09ERSBTdW1tYXJ5IFN0YXRpc3RpY3MiCmF1dGhvcjogIkFhcm9uIFdpbGxpcywgQ2Fubm9uIEJyb29rZSwgSm9zaHVhIEhlbmRlcnNvbiwgUnlhbiBSYWRjbGlmZiIKc3VidGl0bGU6ICJCVVM2OTYgRmluYWwgUHJvamVjdCB2MTYiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKIyBQbGVhc2UgbGVhdmUgdGhpcyBjb2RlIGNodW5rIGFzIGlzLiBJdCBtYWtlcyBzb21lIHNsaWdodCBmb3JtYXR0aW5nIGNoYW5nZXMgdG8gYWx0ZXIgdGhlIG91dHB1dCB0byBiZSBtb3JlIGFlc3RoZXRpY2FsbHkgcGxlYXNpbmcuIAoKbGlicmFyeSgna25pdHInKQoKCiMgQ2hhbmdlIHRoZSBudW1iZXIgaW4gc2V0IHNlZWQgdG8geW91ciBvd24gZmF2b3JpdGUgbnVtYmVyCnNldC5zZWVkKDE4MTgpCm9wdGlvbnMod2lkdGg9NzApCm9wdGlvbnMoc2NpcGVuPTk5KQoKCiMgdGhpcyBzZXRzIHRleHQgb3V0cHV0dGVkIGluIGNvZGUgY2h1bmtzIHRvIHNtYWxsCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLndyYXA9NTApLHRpZHk9VFJVRSwgc2l6ZSA9ICJ2c21hbGwiKSAgCm9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAjICJjYWNoaW5nIiBzdG9yZXMgb2JqZWN0cyBpbiBjb2RlIGNodW5rcyBhbmQgb25seSByZXdyaXRlcyBpZiB5b3UgY2hhbmdlIHRoaW5ncwogICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZG93bmxvYWRzIGRlcGVuZGVuY3kgZmlsZXMKICAgICAgICAgICAgICAgYXV0b2RlcCA9IFRSVUUsCiAgICAgICAgICAgICAgICMgCiAgICAgICAgICAgICAgIGNhY2hlLmNvbW1lbnRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICMgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwKICAgICAgICAgICAgICAgIyBjaGFuZ2UgZmlnLndpZHRoIGFuZCBmaWcuaGVpZ2h0IHRvIGNoYW5nZSB0aGUgY29kZSBoZWlnaHQgYW5kIHdpZHRoIGJ5IGRlZmF1bHQKICAgICAgICAgICAgICAgZmlnLndpZHRoID0gNS41LCAgCiAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA0LjUsCiAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJykKCgpgYGAKCmBgYHtyIHNldHVwLTJ9CgojIEFsd2F5cyBwcmludCB0aGlzIG91dCBiZWZvcmUgeW91ciBhc3NpZ25tZW50CnNlc3Npb25JbmZvKCkKZ2V0d2QoKQoKYGBgCgoKPCEtLSAjIyMgc3RhcnQgYW5zd2VyaW5nIHlvdXIgcHJvYmxlbSBzZXQgaGVyZSAtLT4KPCEtLSBZb3UgbWF5IGV4cG9ydCB5b3VyIGhvbWV3b3JrIGluIGVpdGhlciBodG1sIG9yIHBkZiwgd2l0aCB0aGUgZm9ybWVyIHVzdWFsbHkgYmVpbmcgZWFzaWVyLiAKICAgICBUbyBleHBvcnQgb3IgY29tcGlsZSB5b3VyIFJtZCBmaWxlOiBjbGljayBhYm92ZSBvbiAnS25pdCcgdGhlbiAnS25pdCB0byBIVE1MJyAtLT4KPCEtLSBCZSBzdXJlIHRvIHN1Ym1pdCBib3RoIHlvdXIgLlJtZCBmaWxlIGFuZCB0aGUgY29tcGlsZWQgLmh0bWwgb3IgLnBkZiBmaWxlIGZvciBmdWxsIGNyZWRpdCAtLT4KCgpgYGB7ciBzZXR1cC0zfQoKIyBsb2FkIGFsbCB5b3VyIGxpYnJhcmllcyBpbiB0aGlzIGNodW5rIApsaWJyYXJ5KCd0aWR5dmVyc2UnKQpsaWJyYXJ5KCJmcyIpCmxpYnJhcnkoJ2hlcmUnKQpsaWJyYXJ5KCdkcGx5cicpCmxpYnJhcnkoJ3RpZHl2ZXJzZScpCmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCdnZ3JlcGVsJykKbGlicmFyeSgnZ2d0aGVtZXMnKQpsaWJyYXJ5KCdmb3JjYXRzJykKbGlicmFyeSgncnNhbXBsZScpCmxpYnJhcnkoJ2x1YnJpZGF0ZScpCmxpYnJhcnkoJ2dndGhlbWVzJykKbGlicmFyeSgna2FibGVFeHRyYScpCmxpYnJhcnkoJ3Bhc3RlY3MnKQpsaWJyYXJ5KCd2aXJpZGlzJykKbGlicmFyeSgncGxvdGx5JykKbGlicmFyeSgndGlkeXF1YW50JykKbGlicmFyeSgnc2NhbGVzJykKbGlicmFyeSgiZ2RhdGEiKQoKIyBub3RlLCBkbyBub3QgcnVuIGluc3RhbGwucGFja2FnZXMoKSBpbnNpZGUgYSBjb2RlIGNodW5rLiBpbnN0YWxsIHRoZW0gaW4gdGhlIGNvbnNvbGUgb3V0c2lkZSBvZiBhIGNvZGUgY2h1bmsuIAoKYGBgCgoKCiMjIFBhcnQgMSAtIEZpbmFsIFByb2plY3QgQ2xlYW5pbmcgYW5kIFN1bW1hcnkgU3RhdGlzdGljcyAKCjFhKSBMb2FkaW5nIGRhdGEKCmBgYHtyfQoKI1JlYWRpbmcgdGhlIGRhdGEgaW4gYW5kIGRvaW5nIG1pbm9yIGluaXRpYWwgY2xlYW5pbmcgaW4gdGhlIGZ1bmN0aW9uIGNhbGwKI1JlcHJvZHVjaWJsZSBkYXRhIGFuYWx5c2lzIHNob3VsZCBhdm9pZCBhbGwgYXV0b21hdGljIHN0cmluZyB0byBmYWN0b3IgY29udmVyc2lvbnMuCiNzdHJpcC53aGl0ZSByZW1vdmVzIHdoaXRlIHNwYWNlIAojbmEuc3RyaW5ncyBpcyBhIHN1YnN0aXR1dGlvbiBzbyBhbGwgdGhhdCBoYXZlICIiIHdpbGwgPSBuYQpkYXRhIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiZG9ub3JfZGF0YS5jc3YiKSwKICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgc3RyaXAud2hpdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSAiIikKCgoKYGBgCgoKMWIpIEZpeGluZyB0aGUgd29ua3kgRE9CICYgRGF0YSBjbGVhbnVwCgpgYGB7cn0KCiMoQmlydGhkYXRlIGFuZCBBZ2UsIElEIGFzIGEgbnVtYmVyKWFkZGluZyBET0IgKEFnZS9TcG91c2UgQWdlKSBpbiB5ZWFycyBjb2x1bW5zIGFuZCBhZGRpbmcgdHdvIGZpZWxkcyBmb3IgYXNzaWdubWVudCBhbmQgbnVtYmVyIG9mIGNoaWxkcmVuIGFuZCBudW1iZXIgb2YgZGVncmVlcwpkYXRhY2xlYW4gPC0gZGF0YSAlPiUKICBtdXRhdGUoQmlydGhkYXRlID0gaWZlbHNlKEJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBCaXJ0aGRhdGUpKSAlPiUKICBtdXRhdGUoQmlydGhkYXRlID0gbWR5KEJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShBZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBCaXJ0aGRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBpZmVsc2UoU3BvdXNlLkJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBTcG91c2UuQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBtZHkoU3BvdXNlLkJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShTcG91c2UuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gU3BvdXNlLkJpcnRoZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICBtdXRhdGUoSUQgPSBhcy5udW1lcmljKElEKSkgJT4lIAogIG11dGF0ZShBc3NpZ25tZW50X2ZsYWcgPSBpZmVsc2UoaXMubmEoQXNzaWdubWVudC5OdW1iZXIpLCAwLDEpKSAlPiUgCiAgbXV0YXRlKCBOb19vZl9DaGlsZHJlbiA9IGlmZWxzZShpcy5uYShDaGlsZC4xLklEKSwwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKENoaWxkLjIuSUQpLDEsMikpKSU+JQogbXV0YXRlKElEID0gYXMubnVtZXJpYyhJRCkpICU+JSAKICAgIG11dGF0ZSggbm1iX2RlZ3JlZSA9IGlmZWxzZShpcy5uYShEZWdyZWUuVHlwZS4xKSwwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKERlZ3JlZS5UeXBlLjIpLDEsMikpKSAlPiUKI2NvbmZlcnJhbCBkYXRlcwogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xID0gaWZlbHNlKENvbmZlcnJhbC5EYXRlLjEgPT0gIjAwMDEtMDEtMDEiLCBOQSwgQ29uZmVycmFsLkRhdGUuMSkpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xID0gbWR5KENvbmZlcnJhbC5EYXRlLjEpKSAlPiUKICBtdXRhdGUoQ29uZmVycmFsLkRhdGUuMS5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBDb25mZXJyYWwuRGF0ZS4xLCBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQogIAogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yID0gaWZlbHNlKENvbmZlcnJhbC5EYXRlLjIgPT0gIjAwMDEtMDEtMDEiLCBOQSwgQ29uZmVycmFsLkRhdGUuMikpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yID0gbWR5KENvbmZlcnJhbC5EYXRlLjIpKSAlPiUKICBtdXRhdGUoQ29uZmVycmFsLkRhdGUuMi5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBDb25mZXJyYWwuRGF0ZS4yLCBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQogIAogIG11dGF0ZShMYXN0LkNvbnRhY3QuQnkuQW55b25lID0gaWZlbHNlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPT0gIjAwMDEtMDEtMDEiLCBOQSwgTGFzdC5Db250YWN0LkJ5LkFueW9uZSkpICU+JQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQnkuQW55b25lID0gbWR5KExhc3QuQ29udGFjdC5CeS5BbnlvbmUpKSAlPiUKICBtdXRhdGUoTGFzdC5Db250YWN0LkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IExhc3QuQ29udGFjdC5CeS5BbnlvbmUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiBtdXRhdGUoSEguRmlyc3QuR2lmdC5EYXRlID0gaWZlbHNlKEhILkZpcnN0LkdpZnQuRGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBISC5GaXJzdC5HaWZ0LkRhdGUpKSAlPiUKICBtdXRhdGUoSEguRmlyc3QuR2lmdC5EYXRlID0gbWR5KEhILkZpcnN0LkdpZnQuRGF0ZSkpICU+JQptdXRhdGUoSEguRmlyc3QuR2lmdC5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBISC5GaXJzdC5HaWZ0LkRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lIAojbWFqb3IgZ2lmdCAKICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gaWZlbHNlKExpZmV0aW1lLkdpdmluZyA+IDUwMDAwLCAxLDApICU+JSBmYWN0b3IoLiwgbGV2ZWxzID0gYygiMCIsIjEiKSkpICU+JQojc3BsaXR0aW5nIHVwIHRoZSBhZ2UgaW50byByYW5nZXMgYW5kIGNyZWF0aW5nIGNhdGVnb3J5IGZvciBlYXN5IHZpc3VhbGl6YXRpb24gCiAgbXV0YXRlKGFnZV9yYW5nZSA9IAogICAgaWZlbHNlKEFnZSAlaW4lIDEwOjE5LCAiMTAgPCAyMCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDIwOjI5LCAiMjAgPCAzMCB5ZWFycyBvbGQiLCAKICAgIGlmZWxzZShBZ2UgJWluJSAzMDozOSwgIjMwIDwgNDAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA0MDo0OSwgIjQwIDwgNTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA1MDo1OSwgIjUwIDwgNjAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA2MDo2OSwgIjYwIDwgNzAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA3MDo3OSwgIjcwIDwgODAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA4MDo4OSwgIjgwIDwgOTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA5MDoxMjAsICI5MCsgeWVhcnMgb2xkIiwKICAgIE5BKSkpKSkpKSkpKSAlPiUKI2NyZWF0aW5nIGEgcmVnaW9uIGNvbHVtbiB1c2luZyB0aGUgY291bnR5IGRhdGEgYW5kIHRoZSBPTUIgTVNBIChNZXRyb3BvbGl0YW4gU3RhdGlzdGljYWwgQXJlYSkgZGVmaW5pdGlvbnMKICBtdXRhdGUocmVnaW9uID0gCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gTHVpcyBPYmlzcG8iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJLZXJuIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEJlcm5hcmRpbm8iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBCYXJiYXJhIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiVmVudHVyYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkxvcyBBbmdlbGVzIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiT3JhbmdlIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUml2ZXJzaWRlIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIERpZWdvIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSW1wZXJpYWwiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJLaW5nIiAmIFN0YXRlID09ICJXQSIsICJTZWF0dGxlIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNub2hvbWlzaCIgJiBTdGF0ZSA9PSAiV0EiLCAiU2VhdHRsZSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQaWVyY2UiICYgU3RhdGUgPT0gIldBIiwgIlNlYXR0bGUiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ2xhY2thbWFzIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDb2x1bWJpYSIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTXVsdG5vbWFoIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJXYXNoaW5ndG9uIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJZYW1oaWxsIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDbGFyayIgJiBTdGF0ZSA9PSAiV0EiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2thbWFuaWEiICYgU3RhdGUgPT0gIldBIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkRlbnZlciIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkFyYXBhaG9lIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSmVmZmVyc29uIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQWRhbXMiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJEb3VnbGFzIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQnJvb21maWVsZCIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwgICAgCiAgICBpZmVsc2UoQ291bnR5ID09ICJFbGJlcnQiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQYXJrIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ2xlYXIgQ3JlZWsiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJBbGFtZWRhIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDb250cmEgQ29zdGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1hcmluIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb250ZXJleSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTmFwYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEJlbml0byIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEZyYW5jaXNjbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIE1hdGVvIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBDbGFyYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQ3J1eiIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU29sYW5vIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTb25vbWEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgICAgICAgICBOQSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSAlPiUKICBtdXRhdGUocmVnaW9uID0gCiAgICBpZmVsc2UoQ291bnR5ID09ICJLaW5ncyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUXVlZW5zIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJOZXcgWW9yayIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQnJvbngiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlJpY2htb25kIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJXZXN0Y2hlc3RlciIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQmVyZ2VuIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJIdWRzb24iICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlBhc3NhaWMiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlB1dG5hbSIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUm9ja2xhbmQiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlN1ZmZvbGsiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk5hc3NhdSIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTWlkZGxlc2V4IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb25tb3V0aCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiT2NlYW4iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNvbWVyc2V0IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJFc3NleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiVW5pb24iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1vcnJpcyIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU3Vzc2V4IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJIdW50ZXJkb24iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlBpa2UiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIHJlZ2lvbikpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSAlPiUKCiMgY29kZSBub3IgY2FsIHJlZ2lvbiBhcyBhbGwgb3RoZXJzIGluIENBIG5vdCBhbHJlYWR5IGRlZmluZWQKICBtdXRhdGUocmVnaW9uID0gCiAgICBpZmVsc2UoU3RhdGUgPT0gIkNBIiAmIGlzLm5hKHJlZ2lvbikgPT0gVFJVRSwgIk5vciBDYWwiLCByZWdpb24pKQoKI0luaXRpYWwgUmVtb3ZhbCBvZiBDb2x1bW5zIHRoYXQgcHJvdmlkZSBubyBiZW5lZml0IAoKZGF0YWNsZWFuIDwtIHN1YnNldChkYXRhY2xlYW4sc2VsZWN0ID0gLWMoQXNzaWdubWVudC5OdW1iZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lmhhcy5IaXN0b3JpY2FsLk1uZ3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTdWZmaXgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lk1hbmFnZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlJvbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlRpdGxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5TdGF0dXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTdHJhdGVneQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFByb2dyZXNzLkxldmVsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5Hcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuQ2F0ZWdvcnkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxGdW5kaW5nLk1ldGhvZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUXVhbGlmaWNhdGlvbi5BbW91bnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5BbW91bnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhhcmQuR2lmdC5Ub3RhbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTb2Z0LkNyZWRpdC5Ub3RhbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxUb3RhbC5Bc3NpZ25tZW50LkdpZnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLE5vLm9mLlBsZWRnZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvcG9zYWwuLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC5Ob3RlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxISC5MaWZlLlNwb3VzZS5DcmVkaXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTGFzdC5Db250YWN0LkJ5Lk1hbmFnZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsWC4ub2YuQ29udGFjdHMuQnkuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxEb25vclNlYXJjaC5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxpV2F2ZS5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxXZWFsdGhFbmdpbmUuUmFuZ2UKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUGhpbGFudGhyb3BpYy5Db21taXRtZW50cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpCiNjbGVhbmluZyB1cCB6aXAgY29kZXMgcmVtb3ZpbmcgLTQgYWZ0ZXIgCmRhdGFjbGVhbiRaaXAgPC0gZ3N1YihkYXRhY2xlYW4kWmlwLCBwYXR0ZXJuPSItLioiLCByZXBsYWNlbWVudCA9ICIiKQoKI2FkZGluZyB6aXAgY29kZSBkYXRhIGFuZCBjb2x1bW4gCnppcCA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJmaW5hbF9wcm9qZWN0IiwgIlNhbGFyeV9aaXBjb2RlLmNzdiIpLAogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBzdHJpcC53aGl0ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQoKCiNhZGRpbmcgemlwIHNhbGFyeSBjb2x1bW4KZGF0YWNsZWFuIDwtZGF0YWNsZWFuICU+JQogICAgbXV0YXRlKHppcGNvZGVfc2xyeSA9IFZMT09LVVAoWmlwLCB6aXAsIE5BTUUsIFMxOTAyX0MwM18wMDJFKSkgJT4lCiNzbHJ5IHJhbmdlIAogIG11dGF0ZSh6aXBzbHJ5X3JhbmdlID0gCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDA6ODk5OTksICI5MEstOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA5MDAwMDo5OTk5OSwgIjkwSy05OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDEwMDAwMDoxNDk5OTksICIxMDBLLTE0OUsiLCAKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxNTAwMDA6MTk5OTk5LCAiMTUwSy0xOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAyMDAwMDA6MjQ5OTk5LCAiMjAwSy0yNDlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAyNTAwMDA6Mjk5OTk5LCAiMjUwSy0yOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAzMDAwMDA6MzQ5OTk5LCAiMzAwSy0zNDlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAzNTAwMDA6Mzk5OTk5LCAiMzUwSy0zOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA0MDAwMDA6NDk5OTk5LCAiNDAwSy00OTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA1MDAwMDA6OTk5OTk5LCAiNTAwSy05OTlLIiwKICAgIE5BKSkpKSkpKSkpKSkKCnN1bShpcy5uYShkYXRhY2xlYW4kemlwY29kZV9zbHJ5KSkKCiNhZGRpbmcgc2Nob2xhcnNoaXAgZGF0YSAoeS9uKQpzY2hsciA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJmaW5hbF9wcm9qZWN0IiwgInNjaG9sYXJzaGlwLmNzdiIpLAogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBzdHJpcC53aGl0ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQoKI2FkZGluZyBzY2hvbGFyc2hpcCBjb2x1bW4KZGF0YWNsZWFuIDwtZGF0YWNsZWFuICU+JQogICAgbXV0YXRlKHNjaG9sYXJzaGlwID0gVkxPT0tVUChJRCwgc2NobHIsIElELCBTQ0hPTEFSU0hJUCkpIAoKI3JlcGxhY2luZyBOQSB3aXRoIDAgCiBkYXRhY2xlYW4kc2Nob2xhcnNoaXAgPC0gcmVwbGFjZV9uYShkYXRhY2xlYW4kc2Nob2xhcnNoaXAsJzAnKQogCiNyZXBsYWNpbmcgWSB3aXRoIDEgCmRhdGFjbGVhbiRzY2hvbGFyc2hpcDwtaWZlbHNlKGRhdGFjbGVhbiRzY2hvbGFyc2hpcD09IlkiLDEsMCkKCiNjaGVja2luZyBob3cgbWFueSBhcmUgTgp0YWJsZShkYXRhY2xlYW4kc2Nob2xhcnNoaXApCgoKI2NoZWNraW5nIGFuZCBkZWxldGluZyBzY2hvbGFyc2hpcCBjb2x1bW4gCmNsYXNzKGRhdGFjbGVhbiRzY2hscl9mY3QpCmRhdGFjbGVhbiA9IHN1YnNldChkYXRhY2xlYW4sIHNlbGVjdCA9IC1jKHNjaG9sYXJzaGlwKSkKICAKI2NoZWNraW5nIGZvciBkdXBsaWNhdGVzIE4gPjEgaW5kaWNhdGVzIGEgcmVjb3JkcyB2YWx1ZXMgYXJlIGluIHRoZSBmaWxlIHR3aWNlIApkYXRhY2xlYW4gJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQoKI3JlbW92aW5nIGR1cGxpY2F0ZWQgcmVjb3JkcwpkYXRhY2xlYW4gPC0gdW5pcXVlKGRhdGFjbGVhbikKCiNWZXJpZnlpbmcgbiA9IDEgbm8gSUQgd2l0aCBtdWx0aXBsZSByZWNvcmRzIGNsZWFuZWQgb2YgZHVwZXMKZGF0YWNsZWFuICU+JSBncm91cF9ieShJRCkgJT4lIGNvdW50KCkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKCmBgYAoKCjFkIENyZWF0aW5nIG1hbnkgbWFueSBmYWN0b3IgdmFyaWFibGVzCgpgYGB7cn0KCmRhdGFjbGVhbiA8LSAKICBkYXRhY2xlYW4gJT4lIAogICNTRVgKICBtdXRhdGUoc2V4X2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTZXgpLApzZXhfc2ltcGxlID0gCiAgICBmY3RfbHVtcF9uKFNleCwgbiA9IDQpLAojTUFSUklFRAptYXJyaWVkX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShNYXJyaWVkKSwKICAjRE9OT1IgU0VHTUVOVAogIGRvbm9yc2VnX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShEb25vci5TZWdtZW50KSwKICAgICAgICAgZG9ub3JzZWdfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEb25vci5TZWdtZW50LCBuID0gNCksCiAgI0NPTlRBQ1QgUlVMRQogICAgICAgICBjb250YWN0X2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDb250YWN0LlJ1bGVzKSwKICAgICAgICAgY29udGFjdF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENvbnRhY3QuUnVsZXMsIG4gPSA0KSwKICAjU1BPVVNFIE1BSUwKICAgICAgICAgc3BvbWFpbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoU3BvdXNlLk1haWwuUnVsZXMpLAogICAgICAgICBzcG9tYWlsX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oU3BvdXNlLk1haWwuUnVsZXMsIG4gPSA0KSwKICAjSk9CIFRJVExFCiAgICAgICAgIGpvYnRpdGxlX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShKb2IuVGl0bGUpLAogICAgICAgICBqb2J0aXRsZV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEpvYi5UaXRsZSwgbiA9IDUpLAogICNERUdSRUUgVFlQRSAxCiAgICAgICAgIGRlZzFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERlZ3JlZS5UeXBlLjEpLAogICAgICAgICBkZWcxX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oRGVncmVlLlR5cGUuMSwgbiA9IDUpLAogICNERUdSRUUgVFlQRSAyCiAgICAgICAgIGRlZzJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERlZ3JlZS5UeXBlLjIpLAogICAgICAgICBkZWcyX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oRGVncmVlLlR5cGUuMiwgbiA9IDUpLAogICNNQUpPUiAxCiAgICAgICAgIG1hajFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1ham9yLjEpLAogICAgICAgICBtYWoxX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWFqb3IuMSwgbiA9IDUpLAogICNNQUpPUiAyCiAgICAgICAgIG1hajJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1ham9yLjIpLAogICAgICAgICBtYWoyX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWFqb3IuMiwgbiA9IDUpLAogICNNSU5PUiAxCiAgICAgICAgIG1pbjFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1pbm9yLjEpLAogICAgICAgICBtaW4xX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWlub3IuMSwgbiA9IDUpLAogICNNSU5PUiAyCiAgICAgICAgIG1pbjJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1pbm9yLjIpLAogICAgICAgICBtaW4yX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWlub3IuMiwgbiA9IDUpLAogICNTQ0hPT0wgMQogICAgICAgICBzY2hvb2wxX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTY2hvb2wuMSksCiAgICAgICAgIHNjaG9vbDFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTY2hvb2wuMSwgbiA9IDUpLAogICNTQ0hPT0wgMgogICAgICAgICBzY2hvb2wyX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTY2hvb2wuMiksCiAgICAgICAgIHNjaG9vbDJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTY2hvb2wuMiwgbiA9IDUpLAogICNJTlNUSVRVVElPTiBUWVBFCiAgICAgICAgIGluc3R0eXBlX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShJbnN0aXR1dGlvbi5UeXBlKSwKICAgICAgICAgaW5zdHR5cGVfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihJbnN0aXR1dGlvbi5UeXBlLCBuID0gNSksCiAgI0VYVFJBQ1VSUklDVUxBUgogICAgICAgICBleHRyYV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRXh0cmFjdXJyaWN1bGFyKSwKICAgICAgICAgZXh0cmFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihFeHRyYWN1cnJpY3VsYXIsIG4gPSA1KSwKICAjSEggRklSU1QgR0lGVCBGVU5ECiAgICAgICAgIGhoZmlyc3RnaWZ0X2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShISC5GaXJzdC5HaWZ0LkZ1bmQpLAogICAgICAgICBoaGZpcnN0Z2lmdF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEhILkZpcnN0LkdpZnQuRnVuZCwgbiA9IDUpLAojQ0hJTEQgMSBFTlJPTEwgU1RBVFVTCiAgICAgICAgIGNoMV9lbnJvbGxfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuRW5yb2xsLlN0YXR1cyksCiAgICAgICAgIGNoMV9lbnJvbGxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLkVucm9sbC5TdGF0dXMsIG4gPSA0KSwKI0NISUxEIDEgTUFKT1IKICAgICAgICAgY2gxX21hal9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NYWpvciksCiAgICAgICAgIGNoMV9tYWpfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLk1ham9yLCBuID0gNCksCiNDSElMRCAxIE1JTk9SCiAgICAgICAgIGNoMV9taW5fZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuTWlub3IpLAogICAgICAgICBjaDFfbWluX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMS5NaW5vciwgbiA9IDQpLAojQ0hJTEQgMSBTQ0hPT0wKICAgICAgICAgY2gxX3NjaG9vbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5TY2hvb2wpLAogICAgICAgICBjaDFfc2Nob29sX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMS5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDEgRkVFREVSCiAgICAgICAgIGNoMV9mZWVkZXJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuRmVlZGVyLlNjaG9vbCksCiAgICAgICAgIGNoMV9mZWVkZXJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLkZlZWRlci5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDIgRU5ST0xMIFNUQVRVUwogICAgICAgICBjaDFfZW5yb2xsX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLkVucm9sbC5TdGF0dXMpLAogICAgICAgICBjaDJfZW5yb2xsX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5FbnJvbGwuU3RhdHVzLCBuID0gNCksCiNDSElMRCAyIE1BSk9SCiAgICAgICAgIGNoMl9tYWpfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuTWFqb3IpLAogICAgICAgICBjaDJfbWFqX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NYWpvciwgbiA9IDQpLAojQ0hJTEQgMiBNSU5PUgogICAgICAgICBjaDJfbWluX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLk1pbm9yKSwKICAgICAgICAgY2gyX21pbl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjIuTWlub3IsIG4gPSA0KSwKI0NISUxEIDIgU0NIT09MCiAgICAgICAgIGNoMl9zY2hvb2xfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuU2Nob29sKSwKICAgICAgICAgY2gyX3NjaG9vbF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjIuU2Nob29sLCBuID0gNCksCiNDSElMRCAyIEZFRURFUgogICAgICAgICBjaDJfZmVlZGVyX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLkZlZWRlci5TY2hvb2wpLAogICAgICAgICBjaDJfZmVlZGVyX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5GZWVkZXIuU2Nob29sLCBuID0gNCksCiAgICApCgoKCiNjaGVja2luZyB0byBzZWUgaWYgaXRzIGEgZmFjdG9yCiNjbGFzcyhkYXRhY2xlYW4kc2V4X2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRkb25vcnNlZ19mY3QpCiNjbGFzcyhkYXRhY2xlYW4kY29udGFjdF9mY3QpCiNjbGFzcyhkYXRhY2xlYW4kc3BvbWFpbF9mY3QpCgojY2hlY2tpbmcgbGV2ZWxzCiNsZXZlbHMoZGF0YWNsZWFuJHNleF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJGRvbm9yc2VnX3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kY29udGFjdF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJHNwb21haWxfc2ltcGxlKQojbGV2ZWxzKGRhdGFjbGVhbiRoaGZpcnN0Z2lmdF9zaW1wbGUpCgojY3JlYXRpbmcgYSB0YWJsZSBhZ2FpbnN0IFNleCBjb2x1bW4gCiN0YWJsZShkYXRhY2xlYW4kc2V4X2ZjdCwgZGF0YWNsZWFuJHNleF9zaW1wbGUpCgoKYGBgCgpSZWdpb24gQW5hbHlzaXMKCmBgYHtyfQojZ3JvdXBpbmcgYnkgcmVnaW9uIGFuZCBhbmFseXppbmcgCmRhdGFjbGVhbiAlPiUKICBncm91cF9ieShyZWdpb24pICU+JQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChyZWdpb24pLAogICAgICAgICAgICBtZWFuX3RvdGFsX2dpdiA9IG1lYW4oSEguTGlmZXRpbWUuR2l2aW5nKSkgJT4lCiAgYXJyYW5nZSgtQ291bnQpICU+JQogIGZpbHRlcihDb3VudCA+PSAxMDApICU+JQogIG11dGF0ZShtZWFuX3RvdGFsX2dpdiA9IGRvbGxhcihtZWFuX3RvdGFsX2dpdikpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIlJlZ2lvbiIsICJDb3VudCIsICJNZWFuIEhIIExpZmV0aW1lIEdpdmluZyIpLCBhbGlnbj1yZXAoJ2MnLCAzKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQogIAoKYGBgCgoKRG9ub3JTZWdtZW50IEFuYWx5c2lzCgpgYGB7cn0KI2dyb3VwaW5nIGJ5IGRvbm9yc2VnbWVudCBhbmQgYW5hbHl6aW5nIApkYXRhY2xlYW4gJT4lCiAgZ3JvdXBfYnkoRG9ub3IuU2VnbWVudCkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKERvbm9yLlNlZ21lbnQpLAogICAgICAgICAgICBtZWFuX3RvdGFsX2dpdiA9IG1lYW4oSEguTGlmZXRpbWUuR2l2aW5nKSkgJT4lCiAgYXJyYW5nZSgtQ291bnQpICU+JQogIGZpbHRlcihDb3VudCA+PSAxMDApICU+JQogICNhZGRlZCBzY2FsZXMgcGFja2FnZSB0byBoYXZlIHRoZSB2YWx1ZXMgc2hvdyBpbiBkb2xsYXIgCiAgbXV0YXRlKG1lYW5fdG90YWxfZ2l2ID0gKGRvbGxhcihtZWFuX3RvdGFsX2dpdikpKSAlPiUKICBrYWJsZShjb2wubmFtZXMgPSBjKCJEb25vciBTZWdtZW50IiwgIkNvdW50IiwgIk1lYW4gSEggTGlmZXRpbWUgR2l2aW5nIiksIGFsaWduPXJlcCgnYycsIDMpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKCkZpcnN0IGdpZnQgc2l6ZSAKYGBge3J9CmFxIDwtIHF1YW50aWxlKGRhdGFjbGVhbiRISC5GaXJzdC5HaWZ0LkFtb3VudCwgcHJvYnMgPSBjKC4yNSwuNTAsLjc1LC45LC45OSksIG5hLnJtID0gVFJVRSkKCmFxIDwtIGFzLmRhdGEuZnJhbWUoYXEpCgphcSRhcSA8LSBkb2xsYXIoYXEkYXEpCgphcSAlPiUKICBrYWJsZShjb2wubmFtZXMgPSAiUXVhbnRpbGUiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKQ29uc2VjdXRpdmUgZ2l2aW5nCmBgYHtyfQojY29uc2VjdXRpdmUgeWVhcnMgb2YgZ2l2aW5nIApkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKE1heC5Db25zZWMuRmlzY2FsLlllYXJzID4gMCkgJT4lCiAgZ2dwbG90KGFlcyhNYXguQ29uc2VjLkZpc2NhbC5ZZWFycykpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICIjMDAyODQ1IiwgYmlucyA9IDIwKSArIAogIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJDb25zZWN1dGl2ZSBZZWFycyBvZiBHaXZpbmcgRGlzdHJpYnV0aW9uIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwMDAsNTAwMCkpIAoKCgpgYGAKCkxpZmV0aW1lIGdpdmluZyBiYXNlZCBvbiBudW1iZXIgb2YgY2hpbGRyZW4gCgpgYGB7cn0KZGF0YWNsZWFuICU+JQogIGZpbHRlcihISC5MaWZldGltZS5HaXZpbmcgPD0gMTAwMDApICU+JQogIGZpbHRlcihISC5MaWZldGltZS5HaXZpbmcgPiAwKSAlPiUKICBtdXRhdGUoYE5vX29mX0NoaWxkcmVuYCA9IGFzLmZhY3RvcihgTm9fb2ZfQ2hpbGRyZW5gKSkgJT4lCiAgZ2dwbG90KGFlcyhISC5MaWZldGltZS5HaXZpbmcsIGZpbGwgPSBgTm9fb2ZfQ2hpbGRyZW5gKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMCwxMDAwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAwMDAsNTAwMCkpICsKICBnZ3RpdGxlKCJHaXZpbmcgZGlzdHJpYnV0aW9uIGFuZCBudW1iZXIgb2YgY2hpbGRyZW4iKSsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiMwMDI4NDUiLCAiIzAwY2ZjYyIsICIjZmY5OTczIikpCgoKCmBgYAoKCk1lYW4sIE1lZGlhbiwgYW5kIENvdW50IG9mIEdpdmluZyBpbiBBZ2UgUmFuZ2VzIAoKYGBge3J9CgphZ2VfcmFuZ2VfZ2l2aW5nIDwtIGRhdGFjbGVhbiAlPiUKICBncm91cF9ieShhZ2VfcmFuZ2UpICU+JQogIHN1bW1hcmlzZShhdmdfZ2l2aW5nID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lZF9naXZpbmcgPSBtZWRpYW4oSEguTGlmZXRpbWUuR2l2aW5nLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBhbW91bnRfb2ZfcGVvcGxlX2luX2FnZV9yYW5nZSA9IG4oKSkKCgpnbGltcHNlKGFnZV9yYW5nZV9naXZpbmcpCgpgYGAKCgoKCgojIyBQYXJ0IDIgLSBTdW1tYXJ5IFN0YXRpc3RpY3MKCjJhKSBQbG90dGluZyBhdmVyYWdlIGdpdmluZyBieSBhZ2UgcmFuZ2UgCgoKYGBge3J9CgphZ2VfcmFuZ2VfZ2l2aW5nIDwtCiAgYWdlX3JhbmdlX2dpdmluZyAlPiUKICBtdXRhdGUoYWdlX3JhbmdlID0gZmFjdG9yKGFnZV9yYW5nZSkpCgpnZ3Bsb3QoYWdlX3JhbmdlX2dpdmluZywgYWVzKGFnZV9yYW5nZSwgYXZnX2dpdmluZykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0xKSkgKyBsYWJzKHggPSAiQWdlIFJhbmdlIiwgeSA9ICJBdmVyYWdlIEdpdmluZyIpICsKICAgICAgZ2d0aXRsZSgiQXZlcmFnZSBHaXZpbmcgQ29tcGFyZWQgQWNyb3NzIEFnZSBSYW5nZXMiKQoKCmBgYAoKCjJiKSBDb3VudCBvZiBkb25vcnMgYmFzZWQgb24gYWdlIHJhbmdlIChhbm90aGVyIHdheSB0byBsb29rIGF0IGl0KQoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgCiAgICAgICBhZXMoYWdlX3JhbmdlKSkgKyAKICAgICAgIGdlb21fYmFyKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MSkpICsgCiAgbGFicyh0aXRsZSA9ICJDb3VudCBvZiBBZ2UgUmFuZ2VzIiwgeCA9ICIiLCB5ID0gIiIpCiAgCgpgYGAKCjJjKSBCb3hwbG90IG9mIHRoZSBBZ2UgUmFuZ2VzIEFnYWluc3QgdGhlIExpZmV0aW1lIEdpdmluZyBBbW91bnRzIHdpdGggYSBsb2cgc2NhbGUgYXBwbGllZCAtIHRoZSByZWFzb24gd2UgYXBwbGllZCBsb2cgc2NhbGUgaXMgdG8gcmVzb2x2ZSBpc3N1ZXMgd2l0aCB2aXN1YWxpemF0aW9ucyB0aGF0IHNrZXcgdG93YXJkcyBsYXJnZSB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIAoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgYWVzKGFnZV9yYW5nZSxISC5MaWZldGltZS5HaXZpbmcsZmlsbCA9IGFnZV9yYW5nZSkpICsgCiAgZ2VvbV9ib3hwbG90KAogIG91dGxpZXIuY29sb3VyID0gInJlZCIpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpICArIGxhYnMoeCA9ICJBZ2UgUmFuZ2UiLCB5ID0gIkxpZmV0aW1lIEdpdmluZyBBbW91bnQiKSArCiAgICAgIGdndGl0bGUoIkxpZmV0aW1lIEdpdmluZyBDb21wYXJlZCBBY3Jvc3MgQWdlIFJhbmdlcyIpCiAgCgpgYGAKCjJkKSBTcGxpdHRpbmcgYnkgYWdlIGFuZCBnZW5kZXIgCgoKYGBge3J9CgoKI2NyZWF0aW5nIGJveHBsb3RzIApkYXRhY2xlYW4gJT4lIAogIGZpbHRlcihBZ2UgPCAxMDApICU+JSAjcmVtb3ZpbmcgdGhlIHdlaXJkIG91dGxpZXJzIHRoYXQgYXJlIG92ZXIgMTAwIAogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgJT4lCiAgZ2dwbG90KGFlcyhTZXgsIEFnZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICB0aGVtZV9lY29ub21pc3QoKSArIAogIGdndGl0bGUoIkFnZXMgb2YgRG9ub3JzIEJhc2VkIG9uIEdlbmRlciIpICsgCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkKICAKCmBgYAoKR2l2aW5nIGJ5IGdlbmRlcgoKYGBge3J9CgojcmVtb3ZlIE5BcyBVIFgKCmRhdGFjbGVhbjIgPC0gZGF0YWNsZWFuICU+JQogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgCgpxIDwtIGdncGxvdChkYXRhY2xlYW4yKSAKcSArIHN0YXRfc3VtbWFyeV9iaW4oCiAgYWVzKHkgPSBISC5MaWZldGltZS5HaXZpbmcsIHggPSBTZXgpLCAKICBmdW4ueSA9ICJtZWFuIiwgZ2VvbSA9ICJiYXIiKSAKICAKc3VtbWFyeShkYXRhY2xlYW4kc2V4X3NpbXBsZSkKCmBgYAoKTWVhbiBhZ2UgYnkgZ2VuZGVyCgpgYGB7cn0KI2JyZWFrZG93biBvZiBzZXhzIAp0YWxseShncm91cF9ieShkYXRhY2xlYW4sIFNleCkpCgpzdW1tYXJpemUoZ3JvdXBfYnkoZGF0YWNsZWFuLCBTZXgpLCAKICAgICAgICAgIGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgIGF2Z19hZ2UgPSBtZWFuKEFnZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgIG1lZF9hZ2UgPSBtZWRpYW4oQWdlLCBuYS5ybSA9IFRSVUUpKQoKI2dyb3VwaW5nIGJ5IHNleCBhbmQgYWdlIHJhbmdlIGZvciBzbGlkZXMgCnRhbGx5KGdyb3VwX2J5KGRhdGFjbGVhbiwgU2V4LCBhZ2VfcmFuZ2UpKQoKCgpgYGAKCgoKMmUpIERpc3RyaWJ1dGlvbiBvZiBwZW9wbGUgaW4gdGhlIHN0YXRlcyB0aGF0IHRoZXkgbGl2ZS4KCmBgYHtyfQoKICBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKFN0YXRlID0gaWZlbHNlKFN0YXRlID09ICIgIiwgIk5BIiwgU3RhdGUpKSAlPiUKICBmaWx0ZXIoU3RhdGUgIT0gIk5BIikgJT4lCiAgZ3JvdXBfYnkoU3RhdGUpICU+JQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChTdGF0ZSkpICU+JQogIGZpbHRlcihDb3VudCA+IDgwMCkgJT4lCiAgYXJyYW5nZSgtQ291bnQpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yJ3MgU3RhdGUiLCAiQ291bnQiKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoImNvbmRlbnNlZCIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKCjJmKSBMb29raW5nIGF0IGFsbCBkb25vcnMgZmlyc3QgZ2lmdCBhbW91bnQuIDc1JSBtYWRlIGEgZmlyc3QgZ2lmdCBvZiA8MTAwLiAKCmBgYHtyfQoKIG5vX25vbl9kb25vcnMgPC0gZGF0YWNsZWFuICU+JQogIGZpbHRlcihMaWZldGltZS5HaXZpbmcgIT0gMCkKICAKbmQgPC0gcXVhbnRpbGUobm9fbm9uX2Rvbm9ycyRISC5GaXJzdC5HaWZ0LkFtb3VudCwgcHJvYnMgPSBjKC4yNSwuNTAsLjc1LC45LC45OSksIG5hLnJtID0gVFJVRSkKCm5kIDwtIGFzLmRhdGEuZnJhbWUobmQpCgpuZCAlPiUKICBrYWJsZShjb2wubmFtZXMgPSAiUXVhbnRpbGUiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCiAgCgoKYGBgCgoKCiMjIFBhcnQgMyAtIE1vZGVsaW5nCgpTcGxpdCBkYXRhIGFuZCBjcmVhdGUgYSBuZXcgc2V0IGZvciBlYXNpZXIgYW5hbHlzaXMKCmBgYCB7cn0KCiNjb252ZXJ0aW5nIG1hcnJpZWQgWSBhbmQgTiB0byAxIGFuZCAwIApkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQogICAgICBtdXRhdGUoTWFycmllZF9zaW1wbGUgPSBpZmVsc2UoTWFycmllZCA9PSAiTiIsMCwxKSkKCgpkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQogIG11dGF0ZShoaC5saWZldGltZS5naXZpbmdfZmN0ID0gYXMuZmFjdG9yKEhILkxpZmV0aW1lLkdpdmluZykpICU+JQogIG11dGF0ZShISC5MaWZldGltZS5HaXZpbmcuUGx1cyA9IGxvZyhISC5MaWZldGltZS5HaXZpbmcgKyAxKSkKCgoKI0NyZWF0aW5nIHRoZSB2ZWx2ZXQgc2V0IC0gb25seSB0aGUgYmVzdCBjYW4gZW50ZXIKCmRhdGF2ZWx2ZXQgPC0gc3Vic2V0KGRhdGFjbGVhbixzZWxlY3QgPSAtYygKICBhZ2VfcmFuZ2UsCiAgQXRobGV0aWNzLAogIEJpcnRoZGF0ZSwKICBDYXRlZ29yeS5Db2RlcywKICBDYXRlZ29yeS5EZXNjcmlwdGlvbnMsCiAgQ2hpbGQuMS5FbnJvbGwuU3RhdHVzLAogIENoaWxkLjEuSUQsCiAgQ2hpbGQuMi5JRCwKICBDaGlsZC4xLk1ham9yLAogIENoaWxkLjEuTWlub3IsCiAgQ2hpbGQuMS5TY2hvb2wsCiAgQ2hpbGQuMS5GZWVkZXIuU2Nob29sLAogIENoaWxkLjIuRW5yb2xsLlN0YXR1cywKICBDaGlsZC4yLk1ham9yLAogIENoaWxkLjIuTWlub3IsCiAgQ2hpbGQuMi5TY2hvb2wsCiAgQ2hpbGQuMi5GZWVkZXIuU2Nob29sLAogIGNoMV9lbnJvbGxfZmN0LAogIGNoMV9tYWpfZmN0LAogIGNoMV9taW5fZmN0LAogIGNoMV9zY2hvb2xfZmN0LAogIGNoMV9mZWVkZXJfZmN0LAogIGNoMl9zY2hvb2xfZmN0LAogIGNoMl9mZWVkZXJfZmN0LAogIGNoMl9tYWpfZmN0LAogIGNoMl9taW5fZmN0LAogIENvbnRhY3QuUnVsZXMsCiAgRGVncmVlLlR5cGUuMSwKICBEZWdyZWUuVHlwZS4yLAogIERvbm9yLlNlZ21lbnQsCiAgQ2l0eSwKICBDb3VudHksCiAgQ29uZmVycmFsLkRhdGUuMSwKICBDb25mZXJyYWwuRGF0ZS4yLAogIENvbnRhY3QuUnVsZXMsCiAgY29udGFjdF9mY3QsCiAgRGVncmVlLlR5cGUuMSwKICBEZWdyZWUuVHlwZS4yLAogIGRlZzFfZmN0LAogIGRlZzJfZmN0LAogIGRvbm9yc2VnX2ZjdCwKICBFeHRyYWN1cnJpY3VsYXIsCiAgZXh0cmFfZmN0LAogIEhILkZpcnN0LkdpZnQuRnVuZCwKICBISC5GaXJzdC5HaWZ0LkRhdGUsCiAgaGhmaXJzdGdpZnRfZmN0LAogIGhoLmxpZmV0aW1lLmdpdmluZ19mY3QsCiAgSUQsCiAgSW5zdGl0dXRpb24uVHlwZSwKICBpbnN0dHlwZV9mY3QsCiAgSm9iLlRpdGxlLAogIGpvYnRpdGxlX2ZjdCwKICBMYXN0LkNvbnRhY3QuQnkuQW55b25lLAogIExlZ2FjeUxlYWRlci4uY29tcGFzcy5zY29yZS4sCiAgbWFqb3JfZ2lmdGVyLAogIE1ham9yLjEsCiAgTWFqb3IuMiwKICBNaW5vci4xLAogIE1pbm9yLjIsCiAgbWFqMV9mY3QsCiAgbWFqMl9mY3QsCiAgbWluMV9mY3QsCiAgbWluMl9mY3QsCiAgcmVnaW9uLAogIHppcGNvZGVfc2xyeSwKICBTZXgsCiAgU2Nob2xhcnNoaXAsCiAgU2Nob29sLjEsCiAgU2Nob29sLjIsCiAgc2Nob29sMV9mY3QsCiAgc2Nob29sMl9mY3QsCiAgU3BvdXNlLkJpcnRoZGF0ZSwKICBTcG91c2UuTWFpbC5SdWxlcywKICBzcG9tYWlsX2ZjdCwKICBTdGF0ZSwKICBaaXAsCiAgemlwc2xyeV9yYW5nZQopKQoKI2RhdGF2ZWx2ZXQgPC0KI2RhdGF2ZWx2ZXRbc2FwcGx5KGRhdGF2ZWx2ZXQsIGlzLmNoYXJhY3RlcildIDwtICNsYXBwbHkoZGF0YXZlbHZldFtzYXBwbHkoZGF0YXZlbHZldCwgaXMuY2hhcmFjdGVyKV0sIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3IpCgpsaWJyYXJ5KCJyc2FtcGxlIikKCmRhdGFfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChkYXRhdmVsdmV0LCBwcm9wID0gMC43NSkKCmRhdGFfdHJhaW4gPC0gdHJhaW5pbmcoZGF0YV9zcGxpdCkKZGF0YV90ZXN0IDwtIHRlc3RpbmcoZGF0YV9zcGxpdCkKCmBgYAoKCgpgYGB7cn0KcCA8LSBkYXRhdmVsdmV0ICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg1LDEwMCxieSA9IDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQoKZ2dwbG90bHkocCkKICAKcAoKZ2dwbG90KGRhdGEgPSBkYXRhdmVsdmV0LCBhZXMoeCA9IEFnZSkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ImJsdWUiKSsgeGxpbShjKDIwLDEwMCkpCgogIAoKCmBgYAoKQW5vdGhlciBIaXN0b2dyYW0KCgpgYGB7cn0KCmRhdGF2ZWx2ZXQgJT4lCiAgZmlsdGVyKEFnZSA+PSAxMCkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgZ2dwbG90KGFlcyhBZ2UpKSArIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiIzAwMjg0NSIsIGJpbnMgPSAyMCkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwMDAsMjAwMCkpIAoKYGBgCkFnZSBkaXN0cmlidXRpb24gYnkgZ2VuZGVyIAoKYGBge3J9CiNBZ2UgR2VuZGVyIGZpbHRlcmVkIG91dCBiZWxvdyAxNSBhbmQgYWJvdmUgOTAgLSBhbHNvIHJlbW92ZWQgVSBYIHRoZSB3ZWlyZCB2YWx1ZXMgCmRhdGF2ZWx2ZXQgJT4lCiAgZmlsdGVyKEFnZSA+PSAxNSkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgbXV0YXRlKFNleCA9IGFzLmZhY3RvcihTZXgpKSAlPiUKICBmaWx0ZXIoU2V4ICE9ICJVIikgJT4lCiAgZmlsdGVyKFNleCAhPSAiWCIpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gU2V4KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUpICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkFnZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDUwMDAwLDIwMDApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5OTczIiwgIiMwMGNmY2MiKSkKYGBgCgpEb25vciBhZ2UgZGlzdHJpYnV0aW9uIGJ5IG1hcml0YWwgc3RhdHVzIAoKYGBge3J9CiNBZ2UgTWFyaXRhbCBTdGF0dXMKZGF0YXZlbHZldCAlPiUKICBmaWx0ZXIoQWdlID49IDIwKSAlPiUKICBmaWx0ZXIoQWdlIDw9IDg1KSAlPiUKICBnZ3Bsb3QoYWVzKEFnZSwgZmlsbCA9IE1hcnJpZWQpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyNSkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIGJ5IE1hcml0YWwgU3RhdHVzIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsNTAwMDAsMjAwMCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmZjk5NzMiLCAiIzAwY2ZjYyIpKQpgYGAKCkxpbmVhciBNb2RlbCAKCmBgYHtyfQojVGhlc2Ugd2lsbCBmb2N1cyBvbiBwcmVkaWN0aW5nIHdoZXRoZXIgYSBjb25zdGl0dWVudCBpcyBhIGRvbm9yIG9yIG5vbi1kb25vci4gCgoKbW9kMWxtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiBNYXJyaWVkX3NpbXBsZSwKICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbikKCm1vZDJsbSA8LSBsbSggVG90YWwuR2l2aW5nLlllYXJzIH4gTGlmZXRpbWUuR2l2aW5nLAogICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKbW9kM2xtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiByZWdpb24sCiAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KG1vZDFsbSkKc3VtbWFyeShtb2QybG0pCnN1bW1hcnkobW9kM2xtKQojaW5jcmVhc2luZyB0aGUgZ2l2aW5nIHllYXIgb25lIHllYXIgaW5jcmVhc2UgdG90YWwgZ2l2aW5nIGJ5IDAuMDAzNQoKCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coSEguTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+cmVnaW9uKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIlJlZ2lvbiIpCgoKZ2dwbG90KGRhdGEgPSBkYXRhX3RyYWluLCBhZXMoeCA9IEFnZSwgeSA9IGxvZyhISC5MaWZldGltZS5HaXZpbmcpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5ubWJfZGVncmVlKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIk51bWJlciBvZiBEZWdyZWVzIikKCgpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkZpcnN0LkdpZnQuQW1vdW50KSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+ZG9ub3JzZWdfZmN0KSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIkRvbm9yIFNlZ21lbnQiKQoKI1RoaXMgcGxvdCBhY3R1YWxseSBoYXMgc29tZSBpbnRlcmVzdGluZyByZXN1bHRzCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+Tm9fb2ZfQ2hpbGRyZW4pICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiIyBDaGlsZHJlbiIpCgoKZGF0YV90cmFpbiAlPiUgCiAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lIAogIGdsaW1wc2UoKQoKCmBgYAoKCgpNT1JFIE1PREVMUwoKQmlnIGxvZ2lzdGljIG1vZGVsCgpgYGB7cn0KCiMgU2V0IGZhbWlseSB0byBiaW5vbWlhbCB0byBzZXQgbG9naXN0aWMgZnVuY3Rpb24KIyBSdW4gdGhlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQKCmRvbm9yX2xvZ2l0MSA8LQogIGdsbShoaC5saWZldGltZS5naXZpbmdfZmN0IH4gTWFycmllZF9zaW1wbGUsCiAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsCiAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKc3VtbWFyeShkb25vcl9sb2dpdDEpCgoKZG9ub3JfbG9naXQyIDwtCiAgZ2xtKGhoLmxpZmV0aW1lLmdpdmluZ19mY3QgfiBOb19vZl9DaGlsZHJlbiwKICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KGRvbm9yX2xvZ2l0MikKCgpgYGAKCgoKYGBge3IgbG9nfQoKCiNzdW1tYXJ5KGRhdGFfdHJhaW4kbWFqb3JfZ2lmdGVyKQojQXNzaWdubWVudF9mbGFnIHRha2VuIG91dCAtIG1heSBhZGQgYmFjawoKZG9ub3JfbG9naXQzIDwtCiAgZ2xtKG1ham9yX2dpZnRlciB+IE1hcnJpZWQgKyBOb19vZl9DaGlsZHJlbiArIGRvbm9yc2VnX3NpbXBsZSArICBUb3RhbC5HaXZpbmcuWWVhcnMgKyBubWJfZGVncmVlLAogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkoZG9ub3JfbG9naXQzKQpleHAoZG9ub3JfbG9naXQzJGNvZWZmaWNpZW50cykKCiN0cmFpbmluZyBwcmVkaWN0aW9ucyBmb3IgaW4gc2FtcGxlIHByZWRzIApwcmVkc190cmFpbiA8LSBwcmVkaWN0KGRvbm9yX2xvZ2l0MywgbmV3ZGF0YSA9IGRhdGFfdHJhaW4sIHR5cGUgPSAicmVzcG9uc2UiKSAKCiN0ZXN0IHByZWRpY3RzIGZvciBPT1MgKG91dCBvZiBzYW1wbGUpCnByZWRzX3Rlc3QgPC0gcHJlZGljdChkb25vcl9sb2dpdDMsIG5ld2RhdGEgPSBkYXRhX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQoKaGVhZChwcmVkc190cmFpbikKaGVhZChwcmVkc190ZXN0KQoKCgpyZXN1bHRzX3RyYWluIDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdHJhaW4gICAlPiUgc2VsZWN0KG1ham9yX2dpZnRlcikgJT4lIAogICAgbXV0YXRlKG1ham9yX2dpZnRlciA9IGFzLm51bWVyaWMobWFqb3JfZ2lmdGVyKSksCiAgYENsYXNzMWAgPSAgcHJlZHNfdHJhaW4sCiAgYHR5cGVgID0gcmVwKCJ0cmFpbiIsbGVuZ3RoKHByZWRzX3RyYWluKSkKKQoKcmVzdWx0c190ZXN0IDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdGVzdCAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgCiAgICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gYXMubnVtZXJpYyhtYWpvcl9naWZ0ZXIpKSwKICBgQ2xhc3MxYCA9ICBwcmVkc190ZXN0LAogIGB0eXBlYCA9IHJlcCgidGVzdCIsbGVuZ3RoKHByZWRzX3Rlc3QpKQopCgpyZXN1bHRzIDwtIGJpbmRfcm93cyhyZXN1bHRzX3RyYWluLHJlc3VsdHNfdGVzdCkKCmRpbShyZXN1bHRzX3RyYWluKQpkaW0ocmVzdWx0c190ZXN0KQpkaW0ocmVzdWx0cykKCmxpYnJhcnkoJ3Bsb3RST0MnKQoKcF9wbG90IDwtCiAgZ2dwbG90KHJlc3VsdHMsCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArCiAgZ2VvbV9yb2MobGFiZWxzaXplID0gMi41LAogICAgICAgICAgICNUb29rIHRoZSBsYWJlbHNpemUgZG93biB0byBhdm9pZCBjdXRvZmYKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKICNXZSByZW1vdmVkIHNvbWUgb2YgdGhlIGN1dG9mZnMgdG8gYXZvaWQgdGhlIG1hc2h1cCBuZWFyIHRoZSBvcmlnaW4uCgogICNDaGFuZ2VkIHRoZSB0aGVtZSB0byBhdm9pZCBjdXRvZmYgcGxvdCB2YWx1ZXMuCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyAKICBsYWJzKHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsIAogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArCiAgICAgIGdndGl0bGUoIlJPQyBQbG90OiBUcmFpbmluZyBhbmQgVGVzdCIpCnByaW50KHBfcGxvdCkgCgoKcF90cmFpbiA8LQogIGdncGxvdChyZXN1bHRzX3RyYWluLAogICAgICAgICBhZXMobSA9IENsYXNzMSwgZCA9IG1ham9yX2dpZnRlciwgY29sb3IgPSB0eXBlKSkgKwogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKIAogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgCiAgbGFicyh4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLCAKICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKwogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQoKcF90ZXN0IDwtCiAgZ2dwbG90KHJlc3VsdHNfdGVzdCwKICAgICAgICAgYWVzKG0gPSBDbGFzczEsIGQgPSBtYWpvcl9naWZ0ZXIsIGNvbG9yID0gdHlwZSkpICsKICBnZW9tX3JvYyhsYWJlbHNpemUgPSAzLjUsCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArCiAKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE2KSArIAogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIpICsKICAgICAgZ2d0aXRsZSgiUk9DIFBsb3Q6IFRyYWluaW5nIGFuZCBUZXN0IikKCmBgYAoKYGBge3J9Cgojc3VtbWFyeShkb25vcl9sb2dpdDMpCiNjb2VmKGRvbm9yX2xvZ2l0MykKCgojQ2FsY3VsYXRpbmcgQVVDIG9mIGJvdGgKcHJpbnQoY2FsY19hdWMocF90cmFpbikkQVVDKQpwcmludChjYWxjX2F1YyhwX3Rlc3QpJEFVQykKYGBgCgpSSURHRQoKYGBge3J9CgpsaWJyYXJ5KCdnbG1uZXQnKQpsaWJyYXJ5KCdnbG1uZXRVdGlscycpCgpyaWRnZV9maXQxIDwtIGN2LmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcuUGx1cyB+IHNleF9mY3QgKyBkb25vcnNlZ19mY3QgKyBOb19vZl9DaGlsZHJlbiwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDApCgojQWxwaGEgMCBzZXRzIHRoZSBSaWRnZQpwcmludChyaWRnZV9maXQxKQpwcmludChyaWRnZV9maXQxJGxhbWJkYS5taW4pCgpwcmludChyaWRnZV9maXQxJGxhbWJkYS4xc2UpCnBsb3QocmlkZ2VfZml0MSkKCmBgYAoKQklHIFJJREdFCgpgYGB7cn0KCmxpYnJhcnkoJ2dsbW5ldCcpCmxpYnJhcnkoJ2dsbW5ldFV0aWxzJykKCmRhdGFfdHJhaW4gJT4lIG1hcChsZXZlbHMpICU+JSBtYXAobGVuZ3RoKQoKcmlkZ2VfZml0MiA8LSBjdi5nbG1uZXQoSEguTGlmZXRpbWUuR2l2aW5nLlBsdXMgfiBBZ2UgKyBzY2hvb2wxX3NpbXBsZSArIGluc3R0eXBlX3NpbXBsZSArIGV4dHJhX3NpbXBsZSArIGNoMV9tYWpfc2ltcGxlICsgY2gyX21hal9zaW1wbGUgICsgTWFycmllZF9zaW1wbGUgKyBkb25vcnNlZ19zaW1wbGUgKyBtYXJyaWVkX2ZjdCArIHNleF9zaW1wbGUgKyBubWJfZGVncmVlLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCkKCiNBbHBoYSAwIHNldHMgdGhlIFJpZGdlCnByaW50KHJpZGdlX2ZpdDIpCnByaW50KHJpZGdlX2ZpdDIkbGFtYmRhLm1pbikKCnByaW50KHJpZGdlX2ZpdDIkbGFtYmRhLjFzZSkKcGxvdChyaWRnZV9maXQyKQoKYGBgCgoKTEFTU08KCmBgYHtyfQoKI1VzaW5nIGN2LmdsbW5ldCBmcm9tIGNsYXNzCiNscyhkYXRhX3RyYWluKSAKI2lzLmZhY3RvcihkYXRhX3RyYWluJG1ham9yX2dpZnRlcikKI2dsaW1wc2UoZGF0YV90cmFpbiRMaWZldGltZS5HaXZpbmcpCgojZGF0YV90cmFpbiAlPiUgCiMgIHNlbGVjdF9pZihpcy5mYWN0b3IpICU+JSAKIyAgZ2xpbXBzZSgpCgoKCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KGdsbW5ldFV0aWxzKQpsYXNzb19maXQgPC0gY3YuZ2xtbmV0KEhILkxpZmV0aW1lLkdpdmluZy5QbHVzIH4gc2V4X2ZjdCArIGpvYnRpdGxlX3NpbXBsZSArIG5tYl9kZWdyZWUgKyBzY2hvb2wxX3NpbXBsZSArIGhoZmlyc3RnaWZ0X3NpbXBsZSArIG1hajFfc2ltcGxlICsgZG9ub3JzZWdfc2ltcGxlICsgTm9fb2ZfQ2hpbGRyZW4gKyBNYXJyaWVkLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICNBbHBoYSAxIGZvciBsYXNzbwogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSkKCgpwcmludChsYXNzb19maXQkbGFtYmRhLm1pbikKIwpwcmludChsYXNzb19maXQkbGFtYmRhLjFzZSkKCnBsb3QobGFzc29fZml0KQoKYGBgCgpMQVNTTyAyCgpgYGB7cn0KCgpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShnbG1uZXRVdGlscykKbGFzc29fZml0MiA8LSBjdi5nbG1uZXQoSEguTGlmZXRpbWUuR2l2aW5nLlBsdXMgfiBzZXhfZmN0ICsgQWdlICsgc2Nob29sMV9zaW1wbGUgKyBpbnN0dHlwZV9zaW1wbGUgKyBleHRyYV9zaW1wbGUgKyBNYXJyaWVkICsgZG9ub3JzZWdfc2ltcGxlICsgbm1iX2RlZ3JlZSArIE5vX29mX0NoaWxkcmVuICsgam9idGl0bGVfc2ltcGxlLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICNBbHBoYSAxIGZvciBsYXNzbwogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSkKCgpwcmludChsYXNzb19maXQyJGxhbWJkYS5taW4pCiMKcHJpbnQobGFzc29fZml0MiRsYW1iZGEuMXNlKQoKcGxvdChsYXNzb19maXQyKQoKYGBgCgoKYGBge3J9Cgpjb2VmKGxhc3NvX2ZpdDIpCiNEZWZhdWx0IHNldHRpbmcgaXMgbGFtYmRhLjFzZQoKI0Zyb20gdGhlIGJvb2sgLSBzaG93aW5nIGNvbnZlcmdlbmNlIHdpdGggbGFtYmRhIHZhbHVlcwpwbG90KGxhc3NvX2ZpdDIkZ2xtbmV0LmZpdCwgeHZhcj0ibGFtYmRhIikKI2FibGluZSh2PWxvZyhjKGxhc3NvX2ZpdCRsYW1iZGEubWluLCBsYXNzb19maXQkbGFtYmRhLjFzZSkpLCBsdHk9MikKCmBgYAoKCgoKCmBgYHtyfQoKZW5ldF9tb2QgPC0gY3ZhLmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcuUGx1cyB+IHNleF9mY3QgKyBqb2J0aXRsZV9zaW1wbGUgKyBubWJfZGVncmVlICsgc2Nob29sMV9zaW1wbGUgKyBoaGZpcnN0Z2lmdF9zaW1wbGUgKyBtYWoxX3NpbXBsZSArIGRvbm9yc2VnX3NpbXBsZSArIE5vX29mX0NoaWxkcmVuICsgTWFycmllZCwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IHNlcSgwLDEsIGJ5ID0gMC4xKSkKCnByaW50KGVuZXRfbW9kKQpwbG90KGVuZXRfbW9kKQoKCmBgYAoKRUxBU1RJQ05FVAoKYGBge3IgZWxhc3RpY25ldH0KCm1pbmxvc3NwbG90KGVuZXRfbW9kLCAKICAgICAgICAgICAgY3YudHlwZSA9ICJtaW4iKQoKZ2V0X2FscGhhIDwtIGZ1bmN0aW9uKGZpdCkgewogIGFscGhhIDwtIGZpdCRhbHBoYQogIGVycm9yIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgCiAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKG1vZCkge21pbihtb2QkY3ZtKX0pCiAgYWxwaGFbd2hpY2gubWluKGVycm9yKV0KfQoKZ2V0X21vZGVsX3BhcmFtcyA8LSBmdW5jdGlvbihmaXQpIHsKICBhbHBoYSA8LSBmaXQkYWxwaGEKICBsYW1iZGFNaW4gPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBgW1tgLCAibGFtYmRhLm1pbiIpCiAgbGFtYmRhU0UgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBgW1tgLCAibGFtYmRhLjFzZSIpCiAgZXJyb3IgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBmdW5jdGlvbihtb2QpIHttaW4obW9kJGN2bSl9KQogIGJlc3QgPC0gd2hpY2gubWluKGVycm9yKQogIGRhdGEuZnJhbWUoYWxwaGEgPSBhbHBoYVtiZXN0XSwgbGFtYmRhTWluID0gbGFtYmRhTWluW2Jlc3RdLAogICAgICAgICAgICAgbGFtYmRhU0UgPSBsYW1iZGFTRVtiZXN0XSwgZXJvciA9IGVycm9yW2Jlc3RdKQp9CgpiZXN0X2FscGhhIDwtIGdldF9hbHBoYShlbmV0X21vZCkKcHJpbnQoYmVzdF9hbHBoYSkKZ2V0X21vZGVsX3BhcmFtcyhlbmV0X21vZCkKCmJlc3RfbW9kIDwtIGVuZXRfbW9kJG1vZGxpc3RbW3doaWNoKGVuZXRfbW9kJGFscGhhID09IGJlc3RfYWxwaGEpXV0KCnByaW50KGJlc3RfbW9kKQoKbWlubG9zc3Bsb3QoZW5ldF9tb2QsIGN2LnR5cGUgPSAibWluIikKCmBgYAoKUmlkZ2VzIHBsb3QgLSBjb3VsZCBiZSB1c2VmdWwgZm9yIHBsb3R0aW5nIGRvbmF0aW9ucyB2cyBkb25vciBzZWdtZW50CgpgYGB7cn0KCmxpYnJhcnkoJ2dncmlkZ2VzJykKCnN1bW1hcnkoZGF0YV90cmFpbiR2YXJpYWJsZSkKCmdncGxvdChkYXRhX3RyYWluLCBhZXMoeCA9IEhILkxpZmV0aW1lLkdpdmluZywgeSA9IHJlZ2lvbikpICsgZ2VvbV9kZW5zaXR5X3JpZGdlcyhyZWxfbWluX2hlaWdodCA9IDAuMDA1KSArIHhsaW0oYygyNTAwMCwgMTAwMDAwKSkgKyAKICAgICAgZ2d0aXRsZSgiSEggTGlmZXRpbWUgR2l2aW5nIGJ5IFJlZ2lvbiIpCgpgYGAKCmBgYHtyfQoKbGlicmFyeSgnY29ycnBsb3QnKQoKI3JlbW92aW5nIElEIHppcCBhbmQgbm9ubnVtZXJpYyAKY29ycnBsb3RfZGF0YSA8LSBkYXRhY2xlYW5bLWMoMTo0OCw1Mjo1Niw1ODo2MCw2Myw2Njo2Nyw3MDo3Miw3NDo4MSw4MzoxMzIpXQoKI0NvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gbnVtZXJpYyBkYXRhIHR5cGUKY29udmVydF9mYWMybnVtIDwtIGZ1bmN0aW9uKHgpewogIGFzLm51bWVyaWMoYXMuZmFjdG9yKHgpKQp9Cgpjb3JycGxvdF9kYXRhIDwtIG11dGF0ZV9hdChjb3JycGxvdF9kYXRhLAogICAgICAgICAgICAgICAgICAgICAudmFycyA9IGMoMToxMiksCiAgICAgICAgICAgICAgICAgICAgIC5mdW5zID0gY29udmVydF9mYWMybnVtKQojbWFraW5nIGEgbWF0cml4CmNkX2NvciA8LSBjb3IoY29ycnBsb3RfZGF0YSkKCiNjcmVhdGluZyBjb3JyZWxhdGlvbgpjb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjQkI0NDAwIiwgIiNFRTk5OTAiLCAKICAiI0ZGRkZGRiIsICIjNzdBQUVFIiwgIiM0NDc3QkIiKSkKY29ycnBsb3QoY2RfY29yLCBtZXRob2Q9ImNvbG9yIiwgY29sPWNvbCgxMDApLAogIHR5cGU9Imxvd2VyIiwgYWRkQ29lZi5jb2wgPSAiYmxhY2siLAogIHRsLnBvcz0ibHQiLCB0bC5jb2w9ImJsYWNrIiwgCiAgdGwuY2V4PTAuNywgdGwuc3J0PTQ1LCAKICBudW1iZXIuY2V4PTAuNywKICBkaWFnPUZBTFNFKQoKI2NvcnJlbGF0aW9uIG1hdHJpeAojIHBhaXJzKH5BZ2UgKyBNb250aHMuU2luY2UuTGFzdC5HaWZ0ICsgZG9ub3JzZWdfZmN0ICsgCiMgICAgIG5tYl9kZWdyZWUgKyBOb19vZl9DaGlsZHJlbiArIEhILkZpcnN0LkdpZnQuQWdlICsgSEguRmlyc3QuR2lmdC5BbW91bnQgKyBUb3RhbC5HaXZpbmcuWWVhcnMsCiMgICAgIGNvbCA9IGNvcnJwbG90X2RhdGEkSEguTGlmZXRpbWUuR2l2aW5nLAojICAgICBkYXRhID0gY29ycnBsb3RfZGF0YSwgCiMgICAgIG1haW4gPSAiRG9ub3IgU2NhdHRlciBQbG90IE1hdHJpeCIpCgojd29ydGhsZXNzLi4gCgpnZ3Bsb3QoZGF0YSA9IGNvcnJwbG90X2RhdGEsIGFlcyh4ID0gbm1iX2RlZ3JlZSwgeSA9IEhILkxpZmV0aW1lLkdpdmluZykpICsgCiAgZ2VvbV9wb2ludChhcGxoYSA9IDEvMTApKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0icmVkIikgCgpgYGAKCgpSYW5kb20gRm9yZXN0CgpgYGB7cn0KCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdCcpCgpyZl9maXRfZG9ub3IgPC0gcmFuZG9tRm9yZXN0KExpZmV0aW1lLkdpdmluZyB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSBjbGFzc2lmaWNhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gNywKICAgICAgICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5yb3VnaGZpeCwKICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlPVRSVUUKICAgICAgICAgICAgICAgICAgICAgICApCgpwcmludChyZl9maXRfZG9ub3IpCgoKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9Cgp2YXJJbXBQbG90KHJmX2ZpdF9kb25vciwgc29ydCA9IFRSVUUsIAogICAgICAgICAgIG4udmFyID0gNSwKICAgICAgICAgICB0eXBlID0gMiwgY2xhc3MgPSBOVUxMLCBzY2FsZSA9IFRSVUUsIAogICAgICAgICAgIG1haW4gPSBkZXBhcnNlKHN1YnN0aXR1dGUocmZfZml0X2Rvbm9yKSkpCgoKYGBgCgpgYGB7cn0KCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdEV4cGxhaW5lcicpCgpwbG90X21pbl9kZXB0aF9kaXN0cmlidXRpb24oCiAgcmZfZml0X2Rvbm9yLAogIGsgPSAxMCwKICBtaW5fbm9fb2ZfdHJlZXMgPSAwLAogIG1lYW5fc2FtcGxlID0gInRvcF90cmVlcyIsCiAgbWVhbl9zY2FsZSA9IEZBTFNFLAogIG1lYW5fcm91bmQgPSAyLAogIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIG1pbmltYWwgZGVwdGggYW5kIGl0cyBtZWFuIgopCgpgYGAKCgoKCmBgYHtyfQojU3BsaXR0aW5nIENhdGVnb3J5IG91dCB0byBjaGVjayBpZiB0aGUgY2F0ZWdvcnkgaXMgdXNlZnVsIGZvciBhbmFseXNpcwpkYXRhX2NhdGVnb3J5X3NwbGl0X291dCA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKENhdGVnb3J5LkNvZGVzID0gdHJpbShzdHJzcGxpdChhcy5jaGFyYWN0ZXIoQ2F0ZWdvcnkuQ29kZXMpLCAifCIsIGZpeGVkID0gVFJVRSkpKSAlPiUKICB1bm5lc3QoQ2F0ZWdvcnkuQ29kZXMpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ2F0ZWdvcnkuQ29kZXMsdmFsdWVzX2Zyb20gPUNhdGVnb3J5LkNvZGVzLCB2YWx1ZXNfZm4gPSBsZW5ndGgpCgoKYGBgCg==